Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Install NodeJS.

This app runs on NodeJS, we recommend using [mise](https://mise.jdx.dev) to manage the node version.
This app runs on NodeJS, we recommend using [mise](https://mise.jdx.dev) to manage the node version.

```shellscript
mise use node@lts
Expand Down Expand Up @@ -45,6 +45,7 @@ npx prisma migrate dev --name init
If you adapt the schema run this command again to generate migrations that will be applied to the prod db.

### Run the dev server against the dev db

Run the dev server:

```shellscript
Expand All @@ -70,13 +71,16 @@ npm start
# dependency documentation

## database - used to store the devices you've connected to

[sqlite](https://www.sqlite.org) - the prisma ORM handles database management (schema, migrations, etc...).

## orm - to manage the db

[Prisma ORM](https://www.prisma.io/docs/orm/prisma-schema/overview)

## framework - a reference implementation demonstrating the capabilities of the evolver's HTTP API.
[Remix](https://remix.run/docs) - a fullstack framework, it consists of a Node.js based server, tightly coupled to a react web app through the use of HTTP web standards and react hooks.

[Remix](https://remix.run/docs) - a fullstack framework, it consists of a Node.js based server, tightly coupled to a react web app through the use of HTTP web standards and react hooks.
[React](https://react.dev) - a frontend framework
[Tailwind](https://tailwindcss.com) - a css styling framework
[DaisyUI](https://daisyui.com) - a component library for tailwind, that simplifies composing tailwind classnames by providing generic defaults.
Expand All @@ -102,7 +106,9 @@ Whenever a part of the system relies on network requests or complex user interac
Having trouble with MSW? Refer to this example repo: https://github.com/mswjs/examples/blob/main/examples/with-remix/README.md

### balancing unit and integration tests

https://github.com/remix-run/remix/discussions/5769#discussioncomment-5281163

### e2e tests
in a production system at scale the entire evolver system, would have a simulated and tested as part of its deployment pipeline as close to production as possible. At this stage e2e tests could be run against real hardware. This is out of scope for current stage of research. e2e tests are not included in this software. That said, the front end integration tests could be run without MSW against a real evolver endpoint to achieve this whenever it becomes necessary.

in a production system at scale the entire evolver system, would have a simulated and tested as part of its deployment pipeline as close to production as possible. At this stage e2e tests could be run against real hardware. This is out of scope for current stage of research. e2e tests are not included in this software. That said, the front end integration tests could be run without MSW against a real evolver endpoint to achieve this whenever it becomes necessary.
13 changes: 6 additions & 7 deletions app/components/ExperimentsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Link, useLocation } from "@remix-run/react";
import { Link, useLocation, useParams } from "@remix-run/react";
import { Experiment_Output } from "client";
import clsx from "clsx";

export function ExperimentsTable({
id,
experiments,
}: {
id: string;
experiments: { [key: string]: Experiment_Output };
}) {
const { name, id } = useParams();
const { pathname } = useLocation();
const pathElements = pathname.split("/");
const currentPath = pathElements[pathElements.length - 1];
Expand All @@ -21,7 +20,7 @@ export function ExperimentsTable({
<td className="font-mono">
<Link
className={clsx(pathElements.includes(key) && "underline")}
to={`/devices/${id}/experiments/${key}`}
to={`/devices/${id}/${name}/experiments/${key}`}
>
{key}
</Link>
Expand All @@ -44,22 +43,22 @@ export function ExperimentsTable({
</div>
<Link
className="list-col-grow flex items-center"
to={`/devices/${id}/experiments/${key}/logs#${controllerName}`}
to={`/devices/${id}/${name}/experiments/${key}/logs#${controllerName}`}
>
{controllerName}
</Link>

<div className="join">
<Link
className={clsx("btn btn-outline join-item")}
to={`/devices/${id}/experiments/${key}#${controllerName + "config"}`}
to={`/devices/${id}/${name}/experiments/${key}#${controllerName + "config"}`}
>
config
</Link>

<Link
className={clsx("btn btn-outline join-item")}
to={`/devices/${id}/experiments/${key}/logs#${controllerName}`}
to={`/devices/${id}/${name}/experiments/${key}/logs#${controllerName}`}
>
log
</Link>
Expand Down
28 changes: 16 additions & 12 deletions app/components/HardwareTable.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
import { Link, useLocation } from "@remix-run/react";
import {
Link,
useLocation,
useParams,
useSearchParams,
} from "@remix-run/react";
import { EvolverConfigWithoutDefaults } from "client";
import clsx from "clsx";

export function HardwareTable({
evolverConfig,
id,
hardwareName,
queryParams,
}: {
evolverConfig: EvolverConfigWithoutDefaults;
id: string;
hardwareName: string;
queryParams: URLSearchParams;
}) {
const { pathname } = useLocation();
const { name, id } = useParams();
const [queryParams] = useSearchParams();

const currentPath = pathname.split("/").pop();
let currentVials: string[] = [];
let currentVials: string[] | undefined = [];
if (queryParams.has("vials")) {
currentVials = queryParams.get("vials")?.split(",");
currentVials = queryParams?.get("vials")?.split(",");
}
const evolverHardware = evolverConfig.hardware;

const TableRows = Object.keys(evolverHardware).map((key) => {
const vials = evolverHardware[key]?.config?.vials;

const vialsWithLinks = vials?.map((vial) => {
const linkTo = `/devices/${id}/hardware/${key}/history?vials=${vial}`;
const linkTo = `/devices/${id}/${name}/hardware/${key}/history?vials=${vial}`;
const activeVial =
currentVials.includes(vial.toString()) && hardwareName === key;
currentVials?.includes(vial.toString()) && hardwareName === key;
const vialButtons = (
<Link
key={vial}
Expand Down Expand Up @@ -62,8 +66,8 @@ export function HardwareTable({
key={"all"}
to={
vials
? `/devices/${id}/hardware/${key}/history?vials=${vials?.join(",")}`
: `/devices/${id}/hardware/${key}/history`
? `/devices/${id}/${name}/hardware/${key}/history?vials=${vials?.join(",")}`
: `/devices/${id}/${name}/hardware/${key}/history`
}
>
{" "}
Expand Down Expand Up @@ -95,7 +99,7 @@ export function HardwareTable({
currentPath === "calibrate" &&
"btn-active",
)}
to={`/devices/${id}/hardware/${key}/calibrate`}
to={`/devices/${id}/${name}/hardware/${key}/calibrate`}
>
calibrate
</Link>
Expand Down
15 changes: 5 additions & 10 deletions app/components/VialGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
import { Link } from "@remix-run/react";
import { Link, useParams } from "@remix-run/react";
import clsx from "clsx";
import { useState, useEffect } from "react";

const DataTable = ({
data,
id,
vialIndex,
excludedProperties = [],
filteredProperties = [],
}: {
id: string;
vialIndex: number;
data: { [key: string]: number };
excludedProperties?: string[];
filteredProperties?: string[];
}) => {
const { name, id } = useParams();
return (
<div className="overflow-x-auto">
<table className="table table-xs w-full">
<thead>
<tr>
<th>
<Link to={`/devices/${id}/hardware`}>hardware</Link>{" "}
<Link to={`/devices/${id}/${name}/hardware`}>hardware</Link>{" "}
</th>
<th></th>
<th></th>
Expand All @@ -46,7 +45,7 @@ const DataTable = ({
>
<Link
className="link"
to={`/devices/${id}/hardware/${mainKey}/history?vials=${vialIndex}`}
to={`/devices/${id}/${name}/hardware/${mainKey}/history?vials=${vialIndex}`}
>
{mainKey}
</Link>
Expand All @@ -56,7 +55,7 @@ const DataTable = ({
<td>
<Link
className="link"
to={`/devices/${id}/hardware/${mainKey}/history?properties=${subKey}&vials=${vialIndex}`}
to={`/devices/${id}/${name}/hardware/${mainKey}/history?properties=${subKey}&vials=${vialIndex}`}
>
{subKey}
</Link>
Expand All @@ -81,7 +80,6 @@ const DataTable = ({
export function VialGrid({
vialCount,
stateData,
id,
excludedProperties = [],
}: {
vialCount: number;
Expand Down Expand Up @@ -123,7 +121,6 @@ export function VialGrid({
{hasData && (
<DataTable
data={data}
id={id}
vialIndex={index}
excludedProperties={excludedProperties}
/>
Expand All @@ -138,7 +135,6 @@ export function VialGrid({
export function FilterableVialGrid({
vialCount,
stateData,
id,
excludedProperties = [],
}: {
vialCount: number;
Expand Down Expand Up @@ -211,7 +207,6 @@ export function FilterableVialGrid({
{hasData && (
<DataTable
data={data}
id={id}
vialIndex={index}
excludedProperties={excludedProperties}
filteredProperties={filteredProperties}
Expand Down
11 changes: 5 additions & 6 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { server } from "./mocks/server";
import { ENV } from "./utils/env.server";

const ABORT_DELAY = 5_000;
//https://remix.run/docs/en/2.13.1/start/future-flags#v3_singleFetch
export const streamTimeout = ABORT_DELAY;

if (ENV.NODE_ENV === "test") {
server.listen();
Expand Down Expand Up @@ -104,11 +106,7 @@ function handleBrowserRequest(
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<RemixServer context={remixContext} url={request.url} />,
{
onShellReady() {
shellRendered = true;
Expand Down Expand Up @@ -141,6 +139,7 @@ function handleBrowserRequest(
},
);

setTimeout(abort, ABORT_DELAY);
//https://remix.run/docs/en/2.13.1/start/future-flags#v3_singleFetch
setTimeout(abort, streamTimeout + 1000);
});
}
2 changes: 1 addition & 1 deletion app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ function Document(props: {
<Meta />
<Links />
</head>
<body className="p-4">
<body className="pl-4 pr-4 pb-4">
<GlobalLoading />
{props.children}
<ScrollRestoration />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { EditJson } from "~/components/EditJson.client";
import { ClientOnly } from "remix-utils/client-only";
import { useEffect, useState } from "react";
import { exportData } from "~/utils/exportData";
import { type loader } from "./devices.$id";
import { type loader } from "./devices.$id.$name";
import { handleFileUpload } from "~/utils/handleFileUpload";
import { EvolverConfigWithoutDefaults } from "client";
import { ActionFunctionArgs, redirect } from "@remix-run/node";
Expand All @@ -23,9 +23,9 @@ import { toast as notify } from "react-toastify";
import { db } from "~/utils/db.server";

export const handle = {
breadcrumb: ({ params }: { params: { id: string } }) => {
const { id } = params;
return <Link to={`/devices/${id}/config`}>config</Link>;
breadcrumb: ({ params }: { params: { id: string; name: string } }) => {
const { id, name } = params;
return <Link to={`/devices/${id}/${name}/config`}>config</Link>;
},
};

Expand All @@ -48,6 +48,10 @@ const schema = z.object({
(value) => (value === "" ? undefined : value),
z.string({ required_error: "an id is required" }),
),
name: z.preprocess(
(value) => (value === "" ? undefined : value),
z.string({ required_error: "a name is required" }),
),
// Assume this is valid, client side AJV validation.
data: z.string({ required_error: "an evolver config is required" }),
});
Expand All @@ -61,7 +65,7 @@ export async function action({ request }: ActionFunctionArgs) {
if (submission.status !== "success") {
return submission.reply();
}
const { intent, id, data } = submission.value;
const { intent, id, data, name } = submission.value;

// use the db to get the url for that device id...
const targetDevice = await db.device.findUnique({ where: { device_id: id } });
Expand Down Expand Up @@ -110,7 +114,12 @@ export async function action({ request }: ActionFunctionArgs) {
],
});
}
return redirect(`/devices/${id}/config?mode=view`);
// update the database with the new config's name
await db.device.update({
where: { device_id: id },
data: { name },
});
return redirect(`/devices/${id}/${name}/config?mode=view`);
} catch (error) {
return submission.reply({
formErrors: [
Expand All @@ -135,7 +144,9 @@ export default function DeviceConfig() {
const submit = useSubmit();
const mode = searchParams.get("mode") === "edit" ? "edit" : "view";

const loaderData = useRouteLoaderData<typeof loader>("routes/devices.$id");
const loaderData = useRouteLoaderData<typeof loader>(
"routes/devices.$id.$name",
);
let description;

if (loaderData?.description?.config) {
Expand Down Expand Up @@ -179,7 +190,7 @@ export default function DeviceConfig() {
exportData(evolverConfig);
}}
>
Download
download
</button>
<button
className="btn btn-primary"
Expand All @@ -189,7 +200,7 @@ export default function DeviceConfig() {
setSearchParams(params);
}}
>
Edit
edit
</button>
</div>
)}
Expand All @@ -213,6 +224,8 @@ export default function DeviceConfig() {
notify.dismiss();
const formData = new FormData();
formData.append("id", id ?? "");
// get the name from the updated evolver config.
formData.append("name", updatedEvolverConfig.name);
formData.append(
"intent",
UpdateDeviceIntentEnum.Enum.update_evolver,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ export const handle = {
breadcrumb: ({
params,
}: {
params: { id: string; experiment_id: string };
params: { id: string; experiment_id: string; name: string };
}) => {
const { id, experiment_id } = params;
const { id, experiment_id, name } = params;
return (
<Link to={`/devices/${id}/experiments/${experiment_id}/logs`}>logs</Link>
<Link to={`/devices/${id}/${name}/experiments/${experiment_id}/logs`}>
logs
</Link>
);
},
};
Expand Down
Loading