Skip to content

Commit

Permalink
Merge pull request #223 from allen-cell-animated/feature/error-levels
Browse files Browse the repository at this point in the history
Feature/error levels
  • Loading branch information
meganrm authored Nov 4, 2021
2 parents 00616e5 + bfc73b2 commit 2e1ca9d
Show file tree
Hide file tree
Showing 16 changed files with 171 additions and 63 deletions.
40 changes: 34 additions & 6 deletions examples/Viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import type {
import SimulariumViewer, {
SimulariumController,
RenderStyle,
ErrorLevel,
} from "../src";
import "./style.css";
import { isEqual, findIndex } from "lodash";

import PointSimulator from "./PointSimulator";
import PdbSimulator from "./PdbSimulator";
import CurveSimulator from "./CurveSimulator";
import FrontEndError from "../src/simularium/FrontEndError";

const netConnectionSettings = {
serverIp: "staging-node1-agentviz-backend.cellexplore.net",
Expand Down Expand Up @@ -134,6 +136,20 @@ class Viewer extends React.Component<{}, ViewerState> {
e.preventDefault();
};

public onError = (error: FrontEndError) => {
if (error.level === ErrorLevel.ERROR) {
window.alert(`ERROR, something is broken: ${error.message} ${error.htmlData}`);
} else if (error.level === ErrorLevel.WARNING) {
window.alert(
`User warning, but not terrible: ${error.message} ${error.htmlData}`
);
} else if (error.level === ErrorLevel.INFO) {
console.log(`Just for your info. ${error.message}`);
} else {
console.warn(`This error didn't have a level sent with it: ${error.message}. Should probably be converted to a FrontEndError`)
}
};

public onDrop = (e: Event): void => {
this.onDragOver(e);
const event = e as DragEvent;
Expand All @@ -150,7 +166,12 @@ class Viewer extends React.Component<{}, ViewerState> {
const simulariumFileIndex = findIndex(filesArr, (file) =>
file.name.includes(".simularium")
);
const simulariumFile = JSON.parse(parsedFiles[simulariumFileIndex]);
let simulariumFile;
try {
simulariumFile = JSON.parse(parsedFiles[simulariumFileIndex]);
} catch (error) {
return this.onError(new FrontEndError(error.message));
}
const fileName = filesArr[simulariumFileIndex].name;
const geoAssets = filesArr.reduce((acc, cur, index) => {
if (index !== simulariumFileIndex) {
Expand All @@ -161,8 +182,7 @@ class Viewer extends React.Component<{}, ViewerState> {
simulariumController
.changeFile({ simulariumFile, geoAssets }, fileName)
.catch((error) => {
console.log("Error loading file", error);
window.alert(`Error loading file: ${error.message}`);
this.onError(error)
});
});
};
Expand Down Expand Up @@ -413,13 +433,21 @@ class Viewer extends React.Component<{}, ViewerState> {
<label htmlFor={id}>{id}</label>
<input
type="checkbox"
onClick={(event) => this.turnAgentsOnOff((event.target as HTMLInputElement).value)}
onClick={(event) =>
this.turnAgentsOnOff(
(event.target as HTMLInputElement).value
)
}
value={id}
defaultChecked={true}
/>
<input
type="checkbox"
onClick={(event) => this.turnAgentHighlightsOnOff((event.target as HTMLInputElement).value)}
onClick={(event) =>
this.turnAgentHighlightsOnOff(
(event.target as HTMLInputElement).value
)
}
value={id}
defaultChecked={false}
/>
Expand Down Expand Up @@ -511,7 +539,7 @@ class Viewer extends React.Component<{}, ViewerState> {
agentColors={this.state.agentColors}
hideAllAgents={this.state.hideAllAgents}
showPaths={this.state.showPaths}
onError={(error) => window.alert(error)}
onError={this.onError}
backgroundColor={[0, 0, 0]}
/>
</div>
Expand Down
12 changes: 8 additions & 4 deletions src/controller/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ClientSimulator } from "../simularium/ClientSimulator";
import { IClientSimulatorImpl } from "../simularium/localSimulators/IClientSimulatorImpl";
import { ISimulator } from "../simularium/ISimulator";
import { LocalFileSimulator } from "../simularium/LocalFileSimulator";
import FrontEndError from "../simularium/FrontEndError";

jsLogger.setHandler(jsLogger.createDefaultHandler());

Expand Down Expand Up @@ -46,7 +47,7 @@ export default class SimulariumController {
public tickIntervalLength: number;
public handleTrajectoryInfo: (TrajectoryFileInfo) => void;
public postConnect: () => void;
public onError?: (errorMessage: string) => void;
public onError?: (error: FrontEndError) => void;

private networkEnabled: boolean;
private isPaused: boolean;
Expand Down Expand Up @@ -131,6 +132,7 @@ export default class SimulariumController {
this.onError
);
} else {
// caught in try/catch block, not sent to front end
throw new Error(
"Insufficient data to determine and configure simulator connection"
);
Expand Down Expand Up @@ -158,6 +160,8 @@ export default class SimulariumController {
return this.isFileChanging;
}

// Not called by viewer, but could be called by
// parent app
public connect(): Promise<string> {
if (!this.simulator) {
return Promise.reject(
Expand Down Expand Up @@ -296,13 +300,13 @@ export default class SimulariumController {
this.networkEnabled = true; // This confuses me, because local files also go through this code path
this.isPaused = true;
} else {
// caught in following block, not sent to front end
throw new Error("incomplete simulator config provided");
}
} catch (e) {
const error = e as Error;
this.simulator = undefined;

console.warn(e.message);

console.warn(error.message);
this.networkEnabled = false;
this.isPaused = false;
}
Expand Down
6 changes: 5 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ export type {
VisDataFrame,
} from "./simularium";
export { Orchestrator, RenderStyle, SimulariumController };
export { RemoteSimulator, DummyRemoteSimulator } from "./simularium";
export {
RemoteSimulator,
DummyRemoteSimulator,
ErrorLevel,
} from "./simularium";
export { compareTimes } from "./util";

export default Viewport;
17 changes: 15 additions & 2 deletions src/simularium/FrontEndError.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
export enum ErrorLevel {
ERROR = "error",
INFO = "info",
WARNING = "warning",
}

class FrontEndError extends Error {
public htmlData: string;
constructor(htmlData = "", ...params: string[]) {
public level: ErrorLevel;
constructor(
message: string,
level = ErrorLevel.ERROR,
htmlData = "",
...params: string[]
) {
// Pass remaining arguments (including vendor specific ones) to parent constructor
super(...params);

this.name = "FrontEndError";
this.message = message;
this.htmlData = htmlData;
this.level = level;
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/simularium/LocalFileSimulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
SimulariumFileFormat,
} from "./types";
import { ISimulator } from "./ISimulator";
import FrontEndError from "./FrontEndError";

// a LocalFileSimulator is a ISimulator that plays back the contents of
// a drag-n-drop trajectory file (a SimulariumFileFormat object)
Expand Down Expand Up @@ -103,7 +104,7 @@ export class LocalFileSimulator implements ISimulator {
const { spatialData, trajectoryInfo } = this.simulariumFile;

if (!spatialData) {
const newError = new Error(
const newError = new FrontEndError(
"Simularium files need 'spatialData' array"
);
return Promise.reject(newError);
Expand Down
43 changes: 29 additions & 14 deletions src/simularium/RemoteSimulator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import jsLogger from "js-logger";
import { ILogger } from "js-logger";
import FrontEndError from "./FrontEndError";
import FrontEndError, { ErrorLevel } from "./FrontEndError";

import { ISimulator } from "./ISimulator";
import { TrajectoryFileInfoV2, VisDataMessage } from "./types";
Expand All @@ -13,7 +13,7 @@ interface NetMessage {
// TODO: proposed new NetMessage data type:
// This factors the raw data structure away from the networking and transmission info.
// This allows the data structure to make a bit more sense with respect to typescript typing,
// and also for raw file drag n drop it doesn't need conection info or msgtype.
// and also for raw file drag n drop it doesn't need connection info or msgtype.
// interface NetMessage {
// connId: string; // unique connection to server
// msgType: number; // identifies the data structure of the message
Expand Down Expand Up @@ -76,11 +76,11 @@ export class RemoteSimulator implements ISimulator {
protected lastRequestedFile: string;
public connectionTimeWaited: number;
public connectionRetries: number;
public handleError: (errorMessage: string) => void | (() => void);
public handleError: (error: FrontEndError) => void | (() => void);

public constructor(
opts?: NetConnectionParams,
errorHandler?: (error: string) => void
errorHandler?: (error: FrontEndError) => void
) {
this.webSocket = null;
this.serverIp = opts && opts.serverIp ? opts.serverIp : "localhost";
Expand Down Expand Up @@ -328,6 +328,7 @@ export class RemoteSimulator implements ISimulator {
if (isConnectionSuccessful) {
return CONNECTION_SUCCESS_MSG;
} else {
// caught by functions that call this
throw new Error(CONNECTION_FAIL_MSG);
}
}
Expand All @@ -350,7 +351,10 @@ export class RemoteSimulator implements ISimulator {
"Request to server cannot be made with a closed Websocket connection."
);
this.handleError(
"Connection to server is closed; please try reloading. If the problem persists, the server may be too busy. Please try again at another time."
new FrontEndError(
"Connection to server is closed; please try reloading. If the problem persists, the server may be too busy. Please try again at another time.",
ErrorLevel.ERROR
)
);
}
}
Expand Down Expand Up @@ -392,28 +396,39 @@ export class RemoteSimulator implements ISimulator {
* Trajectory File: No simulation run, stream a result file piecemeal
*
*/
public startRemoteSimPreRun(timeStep: number, numTimeSteps: number): void {
public startRemoteSimPreRun(
timeStep: number,
numTimeSteps: number
): Promise<void> {
const jsonData = {
msgType: NetMessageEnum.ID_VIS_DATA_REQUEST,
mode: PlayBackType.ID_PRE_RUN_SIMULATION,
timeStep: timeStep,
numTimeSteps: numTimeSteps,
};

this.connectToRemoteServer(this.getIp()).then(() => {
this.sendWebSocketRequest(jsonData, "Start Simulation Pre-Run");
});
return this.connectToRemoteServer(this.getIp())
.then(() => {
this.sendWebSocketRequest(jsonData, "Start Simulation Pre-Run");
})
.catch((e) => {
throw new FrontEndError(e.message, ErrorLevel.ERROR);
});
}

public startRemoteSimLive(): void {
public startRemoteSimLive(): Promise<void> {
const jsonData = {
msgType: NetMessageEnum.ID_VIS_DATA_REQUEST,
mode: PlayBackType.ID_LIVE_SIMULATION,
};

this.connectToRemoteServer(this.getIp()).then(() => {
this.sendWebSocketRequest(jsonData, "Start Simulation Live");
});
return this.connectToRemoteServer(this.getIp())
.then(() => {
this.sendWebSocketRequest(jsonData, "Start Simulation Live");
})
.catch((e) => {
throw new FrontEndError(e.message, ErrorLevel.ERROR);
});
}

public startRemoteTrajectoryPlayback(fileName: string): Promise<void> {
Expand All @@ -434,7 +449,7 @@ export class RemoteSimulator implements ISimulator {
);
})
.catch((error) => {
throw new FrontEndError(error);
throw new FrontEndError(error.message, ErrorLevel.ERROR);
});
}

Expand Down
4 changes: 3 additions & 1 deletion src/simularium/SelectionInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ class SelectionInterface {
});
}

// errors can be caught by onError prop to viewer
// errors will be caught in a try/catch block in the viewport
// If an onError can be caught by onError prop to viewer, will be
// sent to the parent app
public parse(idNameMapping: EncodedTypeMapping): void {
this.clear();
if (!idNameMapping) {
Expand Down
Loading

0 comments on commit 2e1ca9d

Please sign in to comment.