All files / src/pages/flowsheet-page/flowsheet/Solving useSolve.tsx

34.42% Statements 21/61
11.11% Branches 2/18
100% Functions 3/3
41.17% Lines 21/51

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139                                        22318x 22318x 22318x 22318x 22318x 22318x     22318x 374x 374x     22318x 6x     22318x 4x                             74x     74x   74x                 72x 72x           72x                               72x                     22318x                                                                          
import { useState } from "react";
import { toast } from "sonner";
import { useUpdateTaskCache } from "@/hooks/cache/useUpdateTaskCache.ts";
import { useFlowsheetAccess } from "@/hooks/flowsheetAccess";
import { useCurrentGroupId } from "@/hooks/flowsheetObjects";
import {
  useTaskCancelledSubscription,
  useTaskCancellingSubscription,
  useTaskCompletedSubscription,
} from "@/hooks/notifications/notificationSubscriptions.ts";
import { useSearchParam } from "@/hooks/searchParams";
import { store } from "@/store/store";
import {
  api,
  TaskStatusEnum as StatusEnum,
  useIdaesSolveCreateMutation,
} from "../../../../api/apiStore.gen";
import { ContentTypes } from "../LeftSideBar/LeftSideBarTabDefinitions";
 
export function useSolve() {
  const [requestSolve] = useIdaesSolveCreateMutation();
  const updateTaskCache = useUpdateTaskCache();
  const [solveRunning, setSolveRunning] = useState(false);
  const groupId = useCurrentGroupId();
  const access = useFlowsheetAccess();
  const [, setContent] = useSearchParam("content");
 
  // Subscribe to notifications of solve task completion
  useTaskCompletedSubscription((message) => {
    setSolveRunning(false);
    console.log("solve task finished:", message); // This is helpful for debugging why the playwright tests are failing to solve, so I recommend leaving it in.
  });
 
  useTaskCancelledSubscription(() => {
    setSolveRunning(false);
  });
 
  useTaskCancellingSubscription(() => {
    setSolveRunning(false);
  });
 
  const solve = async (
    scenarioNumber?: number,
    perform_diagnostics?: boolean,
  ) => {
    if (access?.can_edit === false) {
      toast.warning("This flowsheet is read-only", {
        description: "Make a copy to edit or solve this flowsheet.",
      });
      return;
    }
 
    try {
      setSolveRunning(true);
 
      // Wait for any current mutations to finish before starting the solve, to make sure all updates are complete.
      await Promise.all(store.dispatch(api.util.getRunningMutationsThunk()));
 
      const response = await requestSolve({
        solveRequest: {
          group_id: groupId,
          perform_diagnostics,
          scenario_number: scenarioNumber,
          // is_rating_mode: ratingMode
        },
      }).unwrap();
 
      updateTaskCache(response);
      console.log("Solve task started:", response); // This is helpful for debugging why the playwright tests are failing to solve, so I recommend leaving it in.
 
      if (
        response.status === StatusEnum.Pending ||
        response.status === StatusEnum.Running
      )
        toast.success("Solve started successfully", {
          description: "A flowsheet solve is in progress.",
        });
      else {
        setSolveRunning(false);
        setContent(ContentTypes.solverLogs); // so the user can see the logs immediately
 
        const error = response.error;
        console.error("Error solving with idaes.");
        console.error(error);
 
        toast.error("Solve could not be started", {
          description: solveErrorDescription(error),
        });
      }
 
      return response;
 
      // Known errors are handled at the task processing layer,
      // but we still need to catch unknown errors here
    } catch (error: unknown) {
      setSolveRunning(false);
      toast.error("Solve could not be started", {
        description: solveErrorDescription(error),
      });
    }
  };
  return [solve, solveRunning] as const;
}
 
function solveErrorDescription(error: unknown) {
  const fallback = "Open solver logs for details.";
 
  Iif (!error) return fallback;
  Iif (typeof error === "string") return error;
 
  Iif (typeof error !== "object") return fallback;
 
  const detail = stringField(error, "detail");
  Iif (detail) return detail;
 
  const data = (error as { data?: unknown }).data;
  Iif (typeof data === "string") return data;
  if (data && typeof data === "object") {
    const dataDetail = stringField(data, "detail");
    Iif (dataDetail) return dataDetail;
 
    const dataError = stringField(data, "error");
    Iif (dataError) return dataError;
  }
 
  const cause = stringField(error, "cause");
  const message = stringField(error, "message");
  Iif (cause && message) return `${cause}: ${message}`;
  Iif (message) return message;
  Iif (cause) return cause;
 
  return fallback;
}
 
function stringField(source: object, key: string) {
  const value = (source as Record<string, unknown>)[key];
  return typeof value === "string" ? value : "";
}