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

67.21% Statements 41/61
44.44% Branches 8/18
100% Functions 3/3
78.43% Lines 40/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                                        25741x 25741x 25741x 25741x 25741x 25741x     25741x 340x 340x     25741x 6x     25741x 4x                             80x     80x   80x                 80x 80x           79x       1x 1x   1x 1x 1x   1x         80x                     25741x       1x   1x 1x   1x   1x 1x   1x 1x                 1x 1x 1x               3x 3x    
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");
  if (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 : "";
}