Skip to content

Commit

Permalink
several improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
linomp committed Aug 29, 2024
1 parent 5399821 commit b922e06
Show file tree
Hide file tree
Showing 18 changed files with 110 additions and 202 deletions.
1 change: 0 additions & 1 deletion mvp/client/ui/src/shared/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise';
export { OpenAPI } from './core/OpenAPI';
export type { OpenAPIConfig } from './core/OpenAPI';

export type { GameMetrics } from './models/GameMetrics';
export type { GameParametersDTO } from './models/GameParametersDTO';
export type { GameSessionDTO } from './models/GameSessionDTO';
export type { HighScoreDTO } from './models/HighScoreDTO';
Expand Down
15 changes: 0 additions & 15 deletions mvp/client/ui/src/shared/api/models/GameMetrics.ts

This file was deleted.

16 changes: 0 additions & 16 deletions mvp/client/ui/src/shared/api/services/SessionsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { GameMetrics } from '../models/GameMetrics';
import type { GameSessionDTO } from '../models/GameSessionDTO';
import type { MqttFrontendConnectionDetails } from '../models/MqttFrontendConnectionDetails';

Expand Down Expand Up @@ -49,21 +48,6 @@ sessionId: string,
});
}

/**
* Get Metrics
* @returns GameMetrics Successful Response
* @throws ApiError
*/
public static getMetricsSessionsMetricsGet(): CancelablePromise<GameMetrics> {
return __request(OpenAPI, {
method: 'GET',
url: '/sessions/metrics',
errors: {
404: `Not found`,
},
});
}

/**
* Send Mqtt Heartbeat
* @returns any Successful Response
Expand Down
2 changes: 1 addition & 1 deletion mvp/experiments/MachineWrapperForExperiment.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pandas as pd
from matplotlib.pyplot import figure, show

from mvp.server.core.machine.MachineState import MachineState
from mvp.server.core.game.MachineState import MachineState

SIMULATE_MAINTENANCE = False
SAVE_HISTORY = True
Expand Down
2 changes: 1 addition & 1 deletion mvp/server/core/analysis/rul_prediction.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import numpy as np
import onnxruntime as rt

from mvp.server.core.machine.MachineState import OperationalParameters
from mvp.server.core.game.MachineState import OperationalParameters

# Load the SVR pipeline from ONNX file
onnx_path = "mvp/server/core/analysis/artifacts/svr_pipeline_23_06_24.onnx"
Expand Down
4 changes: 2 additions & 2 deletions mvp/server/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@
REVENUE_PER_DAY = 15
MAINTENANCE_COST = 40
SENSOR_COST = 30
PREDICTION_MODEL_COST = 80
DEMAND_PEAK_EVENT_PROBABILITY = 0.20
PREDICTION_MODEL_COST = 60
DEMAND_PEAK_EVENT_PROBABILITY = 0.1
DEMAND_PEAK_BONUS_MULTIPLIER = 3
49 changes: 0 additions & 49 deletions mvp/server/core/game/GameMetrics.py

This file was deleted.

34 changes: 19 additions & 15 deletions mvp/server/core/game/GameSession.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@

from mvp.server.core.analysis.rul_prediction import default_rul_prediction_fn, svr_rul_prediction_fn
from mvp.server.core.constants import *
from mvp.server.core.game.MachineState import MachineState, OperationalParameters, get_purchasable_sensors, \
get_purchasable_predictions
from mvp.server.core.game.UserMessage import UserMessage
from mvp.server.core.machine.MachineState import MachineState, OperationalParameters

load_dotenv()

FORCE_DEMAND_PEAK_EVENT = os.getenv("DEV_FORCE_DEMAND_PEAK_EVENT", False)
FORCE_QUICK_FINISH = os.getenv("DEV_FORCE_QUICK_FINISH", False)
COLLECT_MACHINE_HISTORY = os.getenv("COLLECT_MACHINE_HISTORY", False)


class GameSession(BaseModel):
id: str
Expand Down Expand Up @@ -42,9 +47,9 @@ def new_game_session(_id: str, _state_publish_function: Callable[["GameSession"]
started_at=datetime.now(),
state_publish_function=_state_publish_function
)
session.available_sensors = {sensor: False for sensor in session.machine_state.get_purchasable_sensors()}
session.available_sensors = {sensor: False for sensor in get_purchasable_sensors()}
session.available_predictions = {prediction: False for prediction in
session.machine_state.get_purchasable_predictions()}
get_purchasable_predictions()}
session.last_updated = datetime.now()

return session
Expand All @@ -64,9 +69,8 @@ def get_total_duration(self) -> float:
def update_game_over_flag(self) -> None:
self.is_game_over = False

if os.getenv("DEV_FORCE_QUICK_FINISH", False):
if self.current_step >= 30:
self.machine_state.health_percentage = -1
if FORCE_QUICK_FINISH and self.current_step >= TIMESTEPS_PER_MOVE:
self.machine_state.health_percentage = -1

if self.machine_state.is_broken():
self.is_game_over = True
Expand All @@ -75,7 +79,7 @@ def update_game_over_flag(self) -> None:
f"{datetime.now()}: GameSession '{self.id}' - machine failed at step {self.current_step} - {self.machine_state}"
)

async def advance_one_turn(self) -> list[MachineState] | None:
async def advance_one_turn(self) -> tuple[MachineState, list[MachineState]]:
collected_machine_states_during_turn = []

self.last_updated = datetime.now()
Expand All @@ -87,7 +91,7 @@ async def advance_one_turn(self) -> list[MachineState] | None:

for s in range(TIMESTEPS_PER_MOVE - 1):

if os.getenv("COLLECT_MACHINE_HISTORY", False):
if COLLECT_MACHINE_HISTORY:
collected_machine_states_during_turn.append(self.machine_state)

self.update_game_over_flag()
Expand All @@ -101,7 +105,7 @@ async def advance_one_turn(self) -> list[MachineState] | None:
self.available_funds += self.cash_multiplier * REVENUE_PER_DAY / (TIMESTEPS_PER_MOVE)

# Publish state every 3 steps (to reduce the load on the MQTT broker)
if s == 0 or self.current_step % 3 == 0:
if s == 0 or self.current_step % 4 == 0:
self.state_publish_function(self)

await asyncio.sleep(GAME_TICK_INTERVAL)
Expand All @@ -112,21 +116,22 @@ async def advance_one_turn(self) -> list[MachineState] | None:

# 😈 probability of bonus multiplier increases with time, when it is also most risky for the player to skip maintenance!
# TODO: organize this better, there are too many things mixed here... probably won't remember what this code does in 1 week!
if ((random.random() / min(1, self.current_step)) < DEMAND_PEAK_EVENT_PROBABILITY) or os.getenv(
"DEV_FORCE_DEMAND_PEAK_EVENT", False):
r = 100 * random.random() / (0.5 * self.current_step)
if (r < DEMAND_PEAK_EVENT_PROBABILITY) or FORCE_DEMAND_PEAK_EVENT:
self.user_messages["demand_peak_bonus"] = UserMessage(
type="INFO",
content=f"Demand Peak! - Skip maintenance and earn {DEMAND_PEAK_BONUS_MULTIPLIER}x cash in the next turn!"
)

if os.getenv("COLLECT_MACHINE_HISTORY", False):
if COLLECT_MACHINE_HISTORY:
self.machine_state_history.extend(
zip(
range(self.current_step - TIMESTEPS_PER_MOVE, self.current_step),
collected_machine_states_during_turn
)
)
return collected_machine_states_during_turn

return self.machine_state, collected_machine_states_during_turn

def do_maintenance(self) -> bool:
if self.available_funds < MAINTENANCE_COST:
Expand Down Expand Up @@ -165,7 +170,6 @@ def purchase_prediction(self, prediction: str) -> bool:
def update_rul_prediction(self) -> None:
self.user_messages.pop("rul_accuracy_warning", None)

# TODO: clean up this stuff; it feels awkward having to iterate when there is only 1 type of prediction...
for prediction, purchased in self.available_predictions.items():
if prediction == 'predicted_rul' and purchased:

Expand All @@ -183,4 +187,4 @@ def update_rul_prediction(self) -> None:

def get_score(self) -> int:
raw_score = self.current_step * self.available_funds
return round(raw_score / 100)
return round(raw_score / 70)
2 changes: 1 addition & 1 deletion mvp/server/core/game/GameSessionDTO.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

from mvp.server.core.constants import *
from mvp.server.core.game.GameSession import GameSession
from mvp.server.core.game.MachineStateDTO import MachineStateDTO
from mvp.server.core.game.UserMessage import UserMessage
from mvp.server.core.machine.MachineStateDTO import MachineStateDTO


class GameSessionDTO(BaseModel):
Expand Down
70 changes: 70 additions & 0 deletions mvp/server/core/game/MachineState.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from typing import Any, Callable

from pydantic import BaseModel

from mvp.server.core.constants import *
from mvp.server.core.game.OperationalParameters import OperationalParameters
from mvp.server.core.math_utils import constrain_from_0_to_100


def get_purchasable_sensors() -> set[str]:
return {"temperature", "oil_age", "mechanical_wear"}


def get_purchasable_predictions() -> set[str]:
return {"predicted_rul"}


class MachineState(BaseModel):
predicted_rul: int | None = None
health_percentage: float
operational_parameters: OperationalParameters

@staticmethod
def new_machine_state() -> "MachineState":
return MachineState(
health_percentage=100,
operational_parameters=OperationalParameters(
temperature=TEMPERATURE_STARTING_POINT,
oil_age=0,
mechanical_wear=0
)
)

def update_parameters(self, timestep: int) -> None:
self.operational_parameters.update(timestep)
self.health_percentage = self.compute_health_percentage(timestep)

def update_prediction(self, timestep: int,
rul_predictor: Callable[[int, OperationalParameters, list[str]], int | None] = None,
available_sensors: list[str] = None
) -> None:
self.predicted_rul = rul_predictor(timestep, self.operational_parameters, available_sensors)

def compute_health_percentage(self, current_timestep: int) -> float:
return self.operational_parameters.compute_health_percentage(current_timestep, self.health_percentage)

def do_maintenance(self) -> None:
self.operational_parameters = OperationalParameters(
temperature=TEMPERATURE_STARTING_POINT,
oil_age=0,
mechanical_wear=self.operational_parameters.mechanical_wear / MECHANICAL_WEAR_REDUCTION_FACTOR_ON_MAINTENANCE
)
self.health_percentage = constrain_from_0_to_100(self.health_percentage * HEALTH_RECOVERY_FACTOR_ON_MAINTENANCE)

def is_broken(self) -> bool:
return self.health_percentage <= 0

@staticmethod
def from_dict(json: dict[str, Any]) -> "MachineState":
return MachineState(
predicted_rul=json.get("predicted_rul", None),
health_percentage=json.get("health_percentage", 0),
operational_parameters=OperationalParameters.from_dict(
json.get("operational_parameters", {})
)
)

def __str__(self) -> str:
return (f"MachineState(predicted_rul={self.predicted_rul}, health_percentage={self.health_percentage}, "
f"operational_parameters={self.operational_parameters})")
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from pydantic import BaseModel

from mvp.server.core.machine.MachineState import MachineState, OperationalParameters
from mvp.server.core.game.MachineState import MachineState, OperationalParameters


class MachineStateDTO(BaseModel):
Expand Down
Loading

0 comments on commit b922e06

Please sign in to comment.