Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97c9eee8c2 | ||
|
|
8b7b91907b | ||
|
|
35864b4de7 | ||
|
|
621cb1913f | ||
|
|
3e3e3f1c0b | ||
|
|
cdf0374020 | ||
|
|
ec406889f7 | ||
|
|
b731a3bc36 |
No files matched your search
@@ -8,6 +8,9 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: electron
|
||||
|
||||
@@ -8,6 +8,9 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
NIX_PATH: nixpkgs=channel:nixos-25.05
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_VERSION: "1.86"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
Generated
+5
-22
@@ -71,7 +71,7 @@
|
||||
"@types/react": "^19.1.16",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@types/react-virtualized": "^9.22.2",
|
||||
"electron": "^37.3.1",
|
||||
"electron": "^40.6.1",
|
||||
"electron-playwright-helpers": "^1.7.1",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
@@ -5196,15 +5196,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "37.10.3",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-37.10.3.tgz",
|
||||
"integrity": "sha512-3IjCGSjQmH50IbW2PFveaTzK+KwcFX9PEhE7KXb9v5IT8cLAiryAN7qezm/XzODhDRlLu0xKG1j8xWBtZ/bx/g==",
|
||||
"version": "40.6.1",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-40.6.1.tgz",
|
||||
"integrity": "sha512-u9YfoixttdauciHV9Ut9Zf3YipJoU093kR1GSYTTXTAXqhiXI0G1A0NnL/f0O2m2UULCXaXMf2W71PloR6V9pQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@electron/get": "^2.0.0",
|
||||
"@types/node": "^22.7.7",
|
||||
"@types/node": "^24.9.0",
|
||||
"extract-zip": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
@@ -5285,16 +5285,6 @@
|
||||
"global-agent": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron/node_modules/@types/node": {
|
||||
"version": "22.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz",
|
||||
"integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron/node_modules/fs-extra": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||
@@ -5330,13 +5320,6 @@
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/electron/node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron/node_modules/universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"@types/react": "^19.1.16",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@types/react-virtualized": "^9.22.2",
|
||||
"electron": "^37.3.1",
|
||||
"electron": "^40.6.1",
|
||||
"electron-playwright-helpers": "^1.7.1",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
|
||||
@@ -20,6 +20,7 @@ import { TouchButton } from "../touch/TouchButton";
|
||||
import { seriesToUPlotData } from "@/lib/timeseries";
|
||||
import { ControlCard } from "@/control/ControlCard";
|
||||
import { getAllTimeSeries } from "./createChart";
|
||||
import { TargetDashOverlay } from "./TargetDashOverlay";
|
||||
|
||||
// Collects lines connected to visible series for rendering
|
||||
const getVisibleLines = (data: any, visibleSeries: boolean[]) => {
|
||||
@@ -595,10 +596,18 @@ export function BigGraph({
|
||||
|
||||
<div className="flex-1 overflow-hidden rounded-b-3xl pt-4">
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`h-full w-full overflow-hidden transition-opacity duration-100`}
|
||||
className={`relative h-full w-full overflow-hidden transition-opacity duration-100`}
|
||||
style={{ backgroundColor: colors.background }}
|
||||
/>
|
||||
>
|
||||
<div ref={containerRef} className="h-full w-full overflow-hidden" />
|
||||
<TargetDashOverlay
|
||||
uplotRef={uplotRef}
|
||||
newData={newData}
|
||||
config={enhancedConfig}
|
||||
selectedTimeWindow={selectedTimeWindow}
|
||||
isLiveMode={isLiveMode}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,411 @@
|
||||
import React, { useEffect, useMemo, useState, useId } from "react";
|
||||
import uPlot from "uplot";
|
||||
import { BigGraphProps, GraphConfig } from "./types";
|
||||
import { getAllTimeSeries } from "./createChart";
|
||||
|
||||
type OverlayLine = {
|
||||
key: string;
|
||||
color: string;
|
||||
width: number;
|
||||
dash: number[];
|
||||
d: string;
|
||||
};
|
||||
|
||||
type ClipRect = {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
function findFirstGe(arr: number[], value: number): number {
|
||||
let lo = 0;
|
||||
let hi = arr.length - 1;
|
||||
let ans = arr.length;
|
||||
|
||||
while (lo <= hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (arr[mid] >= value) {
|
||||
ans = mid;
|
||||
hi = mid - 1;
|
||||
} else {
|
||||
lo = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ans;
|
||||
}
|
||||
|
||||
function findLastLe(arr: number[], value: number): number {
|
||||
let lo = 0;
|
||||
let hi = arr.length - 1;
|
||||
let ans = -1;
|
||||
|
||||
while (lo <= hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (arr[mid] <= value) {
|
||||
ans = mid;
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ans;
|
||||
}
|
||||
|
||||
function buildSteppedPath(
|
||||
u: uPlot,
|
||||
xData: number[],
|
||||
yData: Array<number | null>,
|
||||
): string {
|
||||
const xMin = u.scales.x?.min;
|
||||
const xMax = u.scales.x?.max;
|
||||
|
||||
if (xData.length < 2 || xMin === undefined || xMax === undefined) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const firstVisibleIdx = Math.max(0, findFirstGe(xData, xMin) - 1);
|
||||
const lastVisibleIdx = Math.min(
|
||||
xData.length - 1,
|
||||
findLastLe(xData, xMax) + 1,
|
||||
);
|
||||
|
||||
if (lastVisibleIdx < firstVisibleIdx) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const parts: string[] = [];
|
||||
let started = false;
|
||||
let prevX = 0;
|
||||
let prevY = 0;
|
||||
|
||||
for (let i = firstVisibleIdx; i <= lastVisibleIdx; i++) {
|
||||
const value = yData[i];
|
||||
if (value === null || value === undefined) continue;
|
||||
|
||||
const x = u.valToPos(xData[i], "x", true);
|
||||
const y = u.valToPos(value, "y", true);
|
||||
|
||||
if (!started) {
|
||||
parts.push(`M ${x} ${y}`);
|
||||
started = true;
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x !== prevX) {
|
||||
parts.push(`L ${x} ${prevY}`);
|
||||
}
|
||||
if (y !== prevY) {
|
||||
parts.push(`L ${x} ${y}`);
|
||||
}
|
||||
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
|
||||
return parts.join(" ");
|
||||
}
|
||||
|
||||
function buildFlatLatestValuePath(
|
||||
u: uPlot,
|
||||
yData: Array<number | null>,
|
||||
): string {
|
||||
let latestValue: number | null = null;
|
||||
|
||||
for (let i = yData.length - 1; i >= 0; i--) {
|
||||
const value = yData[i];
|
||||
if (value !== null && value !== undefined) {
|
||||
latestValue = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (latestValue === null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const y = u.valToPos(latestValue, "y", true);
|
||||
const left = u.bbox.left;
|
||||
const right = u.bbox.left + u.bbox.width;
|
||||
|
||||
return `M ${left} ${y} L ${right} ${y}`;
|
||||
}
|
||||
|
||||
function getHistoricalDashTargets(
|
||||
data: BigGraphProps["newData"],
|
||||
config: GraphConfig,
|
||||
): Array<{
|
||||
dataIndex: number;
|
||||
dash: number[];
|
||||
color: string;
|
||||
width: number;
|
||||
}> {
|
||||
const allOriginalSeries = getAllTimeSeries(data);
|
||||
const firstConfigLineDataIndex = 1 + allOriginalSeries.length;
|
||||
let visibleLineIndex = 0;
|
||||
|
||||
const targets: Array<{
|
||||
dataIndex: number;
|
||||
dash: number[];
|
||||
color: string;
|
||||
width: number;
|
||||
}> = [];
|
||||
|
||||
config.lines?.forEach((line) => {
|
||||
if (line.show === false) return;
|
||||
|
||||
const dash = line.dash ?? (line.type === "threshold" ? [5, 5] : undefined);
|
||||
const isHistoricalDashedTarget = !!line.targetSeries && !!dash?.length;
|
||||
|
||||
if (isHistoricalDashedTarget) {
|
||||
targets.push({
|
||||
dataIndex: firstConfigLineDataIndex + visibleLineIndex,
|
||||
dash: dash!,
|
||||
color: line.color,
|
||||
width: line.width ?? 1,
|
||||
});
|
||||
}
|
||||
|
||||
visibleLineIndex++;
|
||||
});
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
function areDashArraysEqual(a: number[], b: number[]): boolean {
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function areOverlayLinesEqual(a: OverlayLine[], b: OverlayLine[]): boolean {
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (
|
||||
a[i].key !== b[i].key ||
|
||||
a[i].color !== b[i].color ||
|
||||
a[i].width !== b[i].width ||
|
||||
a[i].d !== b[i].d ||
|
||||
!areDashArraysEqual(a[i].dash, b[i].dash)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isSameClipRect(a: ClipRect | null, b: ClipRect): boolean {
|
||||
return (
|
||||
!!a &&
|
||||
a.x === b.x &&
|
||||
a.y === b.y &&
|
||||
a.width === b.width &&
|
||||
a.height === b.height
|
||||
);
|
||||
}
|
||||
|
||||
export function TargetDashOverlay({
|
||||
uplotRef,
|
||||
newData,
|
||||
config,
|
||||
selectedTimeWindow,
|
||||
isLiveMode,
|
||||
}: {
|
||||
uplotRef: React.RefObject<uPlot | null>;
|
||||
newData: BigGraphProps["newData"];
|
||||
config: GraphConfig;
|
||||
selectedTimeWindow: number | "all";
|
||||
isLiveMode: boolean;
|
||||
}) {
|
||||
const targetMeta = useMemo(
|
||||
() => getHistoricalDashTargets(newData, config),
|
||||
[newData, config],
|
||||
);
|
||||
const clipPathId = useId().replace(/:/g, "");
|
||||
|
||||
const [lines, setLines] = useState<OverlayLine[]>([]);
|
||||
const [clipRect, setClipRect] = useState<ClipRect | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (targetMeta.length === 0) {
|
||||
setLines([]);
|
||||
setClipRect(null);
|
||||
}
|
||||
}, [targetMeta]);
|
||||
|
||||
useEffect(() => {
|
||||
let rafId: number | null = null;
|
||||
let bootstrapRafId: number | null = null;
|
||||
let recalcScheduled = false;
|
||||
let hooksAttached = false;
|
||||
const removeHookFns: Array<() => void> = [];
|
||||
let resizeObserver: ResizeObserver | null = null;
|
||||
|
||||
const recalc = () => {
|
||||
recalcScheduled = false;
|
||||
const u = uplotRef.current;
|
||||
if (!u || targetMeta.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const xData = u.data[0] as number[] | undefined;
|
||||
if (!xData || xData.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextLines = targetMeta
|
||||
.map((meta, index) => {
|
||||
const yData = u.data[meta.dataIndex] as
|
||||
| Array<number | null>
|
||||
| undefined;
|
||||
if (!yData || yData.length < 2) return null;
|
||||
|
||||
const useFlatShortWindowPath =
|
||||
isLiveMode &&
|
||||
selectedTimeWindow !== "all" &&
|
||||
selectedTimeWindow <= 60_000;
|
||||
|
||||
const d = useFlatShortWindowPath
|
||||
? buildFlatLatestValuePath(u, yData)
|
||||
: buildSteppedPath(u, xData, yData);
|
||||
if (!d) return null;
|
||||
|
||||
return {
|
||||
key: `${meta.dataIndex}-${index}`,
|
||||
color: meta.color,
|
||||
width: meta.width,
|
||||
dash: meta.dash,
|
||||
d,
|
||||
} as OverlayLine;
|
||||
})
|
||||
.filter((line): line is OverlayLine => !!line);
|
||||
|
||||
if (nextLines.length > 0) {
|
||||
setLines((prev) =>
|
||||
areOverlayLinesEqual(prev, nextLines) ? prev : nextLines,
|
||||
);
|
||||
const nextClipRect: ClipRect = {
|
||||
x: u.bbox.left,
|
||||
y: u.bbox.top,
|
||||
width: u.bbox.width,
|
||||
height: u.bbox.height,
|
||||
};
|
||||
setClipRect((prev) =>
|
||||
isSameClipRect(prev, nextClipRect) ? prev : nextClipRect,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const scheduleRecalc = () => {
|
||||
if (recalcScheduled) return;
|
||||
recalcScheduled = true;
|
||||
rafId = window.requestAnimationFrame(recalc);
|
||||
};
|
||||
|
||||
scheduleRecalc();
|
||||
|
||||
if (targetMeta.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const addHook = (u: any, hookName: string, fn: () => void) => {
|
||||
const hooks = u.hooks?.[hookName];
|
||||
if (!Array.isArray(hooks)) return;
|
||||
hooks.push(fn);
|
||||
removeHookFns.push(() => {
|
||||
const idx = hooks.indexOf(fn);
|
||||
if (idx >= 0) hooks.splice(idx, 1);
|
||||
});
|
||||
};
|
||||
|
||||
const onWindowResize = () => scheduleRecalc();
|
||||
window.addEventListener("resize", onWindowResize);
|
||||
|
||||
const attachHooks = (): boolean => {
|
||||
if (hooksAttached) return true;
|
||||
const u = uplotRef.current as any;
|
||||
if (!u) return false;
|
||||
|
||||
addHook(u, "draw", scheduleRecalc);
|
||||
addHook(u, "setScale", scheduleRecalc);
|
||||
addHook(u, "setData", scheduleRecalc);
|
||||
|
||||
const rootElement = u.root as HTMLElement | undefined;
|
||||
if (rootElement && typeof ResizeObserver !== "undefined") {
|
||||
resizeObserver = new ResizeObserver(() => scheduleRecalc());
|
||||
resizeObserver.observe(rootElement);
|
||||
}
|
||||
|
||||
hooksAttached = true;
|
||||
scheduleRecalc();
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!attachHooks()) {
|
||||
const tryAttach = () => {
|
||||
if (attachHooks()) return;
|
||||
bootstrapRafId = window.requestAnimationFrame(tryAttach);
|
||||
};
|
||||
bootstrapRafId = window.requestAnimationFrame(tryAttach);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (rafId !== null) {
|
||||
window.cancelAnimationFrame(rafId);
|
||||
}
|
||||
if (bootstrapRafId !== null) {
|
||||
window.cancelAnimationFrame(bootstrapRafId);
|
||||
}
|
||||
removeHookFns.forEach((fn) => fn());
|
||||
window.removeEventListener("resize", onWindowResize);
|
||||
resizeObserver?.disconnect();
|
||||
};
|
||||
}, [uplotRef, targetMeta, isLiveMode, selectedTimeWindow]);
|
||||
|
||||
if (lines.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<svg
|
||||
className="pointer-events-none absolute inset-0 h-full w-full"
|
||||
aria-hidden
|
||||
>
|
||||
{clipRect && (
|
||||
<defs>
|
||||
<clipPath id={clipPathId}>
|
||||
<rect
|
||||
x={clipRect.x}
|
||||
y={clipRect.y}
|
||||
width={clipRect.width}
|
||||
height={clipRect.height}
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
)}
|
||||
|
||||
<g clipPath={clipRect ? `url(#${clipPathId})` : undefined}>
|
||||
{lines.map((line) => {
|
||||
return (
|
||||
<path
|
||||
key={line.key}
|
||||
d={line.d}
|
||||
fill="none"
|
||||
stroke={line.color}
|
||||
strokeWidth={line.width}
|
||||
strokeLinecap="butt"
|
||||
strokeDasharray={line.dash.join(" ")}
|
||||
strokeDashoffset={0}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -12,15 +12,6 @@ import {
|
||||
import { BigGraphProps, CreateChartParams } from "./types";
|
||||
import { normalizeDataSeries } from "./animation";
|
||||
|
||||
type CustomDashedTargetLine = {
|
||||
dataIndex: number;
|
||||
dash: number[];
|
||||
color: string;
|
||||
width: number;
|
||||
};
|
||||
|
||||
const DASH_FLOW_PX_PER_SEC = 180;
|
||||
|
||||
export function createChart({
|
||||
containerRef,
|
||||
uplotRef,
|
||||
@@ -161,8 +152,6 @@ export function createChart({
|
||||
|
||||
// Build series configuration for ALL series (but control visibility)
|
||||
const seriesConfig: uPlot.Series[] = [{ label: "Time" }];
|
||||
const customDashedTargetLines: CustomDashedTargetLine[] = [];
|
||||
const dashFlowStartTime = performance.now();
|
||||
|
||||
// Add ALL data series with visibility control
|
||||
const defaultColors = ["#3b82f6", "#ef4444", "#10b981", "#f59e0b", "#8b5cf6"];
|
||||
@@ -188,35 +177,21 @@ export function createChart({
|
||||
});
|
||||
|
||||
// Add config lines
|
||||
let visibleLineIndex = 0;
|
||||
const firstConfigLineDataIndex = 1 + allOriginalSeries.length;
|
||||
config.lines?.forEach((line) => {
|
||||
if (line.show !== false) {
|
||||
const dash =
|
||||
line.dash ?? (line.type === "threshold" ? [5, 5] : undefined);
|
||||
const lineDataIndex = firstConfigLineDataIndex + visibleLineIndex;
|
||||
const isHistoricalDashedTarget = !!line.targetSeries && !!dash?.length;
|
||||
|
||||
if (isHistoricalDashedTarget) {
|
||||
customDashedTargetLines.push({
|
||||
dataIndex: lineDataIndex,
|
||||
dash: dash!,
|
||||
color: line.color,
|
||||
width: line.width ?? 1,
|
||||
});
|
||||
}
|
||||
|
||||
seriesConfig.push({
|
||||
label: line.label,
|
||||
// Historical dashed targets are rendered manually in draw hook to
|
||||
// avoid dash shimmer while the live x-axis slides.
|
||||
// Historical dashed targets are rendered in an SVG overlay.
|
||||
stroke: isHistoricalDashedTarget ? "rgba(0,0,0,0)" : line.color,
|
||||
width: line.width ?? 1,
|
||||
dash: isHistoricalDashedTarget ? undefined : dash,
|
||||
show: true,
|
||||
points: { show: false },
|
||||
});
|
||||
visibleLineIndex++;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -346,119 +321,6 @@ export function createChart({
|
||||
}
|
||||
},
|
||||
],
|
||||
draw: [
|
||||
(u) => {
|
||||
if (customDashedTargetLines.length === 0) return;
|
||||
const xData = u.data[0] as number[] | undefined;
|
||||
if (!xData || xData.length < 2) return;
|
||||
|
||||
const xMin = u.scales.x?.min;
|
||||
const xMax = u.scales.x?.max;
|
||||
if (xMin === undefined || xMax === undefined) return;
|
||||
|
||||
const ctx = u.ctx;
|
||||
const left = u.bbox.left;
|
||||
const top = u.bbox.top;
|
||||
const snap = (v: number) => Math.round(v) + 0.5;
|
||||
const findFirstGe = (arr: number[], value: number): number => {
|
||||
let lo = 0;
|
||||
let hi = arr.length - 1;
|
||||
let ans = arr.length;
|
||||
while (lo <= hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (arr[mid] >= value) {
|
||||
ans = mid;
|
||||
hi = mid - 1;
|
||||
} else {
|
||||
lo = mid + 1;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
};
|
||||
const findLastLe = (arr: number[], value: number): number => {
|
||||
let lo = 0;
|
||||
let hi = arr.length - 1;
|
||||
let ans = -1;
|
||||
while (lo <= hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (arr[mid] <= value) {
|
||||
ans = mid;
|
||||
lo = mid + 1;
|
||||
} else {
|
||||
hi = mid - 1;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
};
|
||||
const firstVisibleIdx = Math.max(0, findFirstGe(xData, xMin) - 1);
|
||||
const lastVisibleIdx = Math.min(
|
||||
xData.length - 1,
|
||||
findLastLe(xData, xMax) + 1,
|
||||
);
|
||||
if (lastVisibleIdx < firstVisibleIdx) return;
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.rect(left, top, u.bbox.width, u.bbox.height);
|
||||
ctx.clip();
|
||||
ctx.lineCap = "butt";
|
||||
|
||||
customDashedTargetLines.forEach(
|
||||
({ dataIndex, dash, color, width }) => {
|
||||
const yData = u.data[dataIndex] as
|
||||
| Array<number | null>
|
||||
| undefined;
|
||||
if (!yData || yData.length < 2) return;
|
||||
|
||||
const dashPeriod = dash.reduce((acc, curr) => acc + curr, 0);
|
||||
ctx.setLineDash(dash);
|
||||
const elapsedMs = performance.now() - dashFlowStartTime;
|
||||
const dashTravelPx = (elapsedMs * DASH_FLOW_PX_PER_SEC) / 1000;
|
||||
ctx.lineDashOffset =
|
||||
dashPeriod > 0 ? -(dashTravelPx % dashPeriod) : 0;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = width;
|
||||
ctx.beginPath();
|
||||
|
||||
let started = false;
|
||||
let prevX = 0;
|
||||
let prevY = 0;
|
||||
|
||||
for (let i = firstVisibleIdx; i <= lastVisibleIdx; i++) {
|
||||
const value = yData[i];
|
||||
if (value === null || value === undefined) continue;
|
||||
|
||||
const x = snap(u.valToPos(xData[i], "x", true));
|
||||
const y = snap(u.valToPos(value, "y", true));
|
||||
|
||||
if (!started) {
|
||||
ctx.moveTo(x, y);
|
||||
started = true;
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x !== prevX) {
|
||||
ctx.lineTo(x, prevY);
|
||||
}
|
||||
if (y !== prevY) {
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
|
||||
if (started) {
|
||||
ctx.stroke();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ctx.restore();
|
||||
},
|
||||
],
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
@@ -660,25 +522,12 @@ export function createChart({
|
||||
|
||||
const cleanup = attachEventHandlers(containerRef.current, handlers);
|
||||
|
||||
let dashFlowAnimationFrame: number | null = null;
|
||||
if (customDashedTargetLines.length > 0) {
|
||||
const animateDashFlow = () => {
|
||||
if (!uplotRef.current) return;
|
||||
uplotRef.current.redraw();
|
||||
dashFlowAnimationFrame = requestAnimationFrame(animateDashFlow);
|
||||
};
|
||||
dashFlowAnimationFrame = requestAnimationFrame(animateDashFlow);
|
||||
}
|
||||
|
||||
animationRefs.lastRenderedData.current = {
|
||||
timestamps: [...timestamps],
|
||||
values: [...primaryValues],
|
||||
};
|
||||
|
||||
return () => {
|
||||
if (dashFlowAnimationFrame !== null) {
|
||||
cancelAnimationFrame(dashFlowAnimationFrame);
|
||||
}
|
||||
cleanup();
|
||||
};
|
||||
}
|
||||
|
||||
File renamed without changes.
File renamed without changes.
+2
-2
@@ -12,9 +12,9 @@ import {
|
||||
useAnalogInputTestMachineNamespace,
|
||||
} from "./useAnalogInputTestMachineNamespace";
|
||||
import { toastError } from "@/components/Toast";
|
||||
import { MachineIdentificationUnique } from "../types";
|
||||
import { MachineIdentificationUnique } from "../../types";
|
||||
import { analogInputTestMachineSerialRoute } from "@/routes/routes";
|
||||
import { analogInputTestMachine } from "../properties";
|
||||
import { analogInputTestMachine } from "../../properties";
|
||||
import { useStateOptimistic } from "@/lib/useStateOptimistic";
|
||||
import { produce } from "immer";
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -14,7 +14,7 @@ import {
|
||||
NamespaceId,
|
||||
createNamespaceHookImplementation,
|
||||
ThrottledStoreUpdater,
|
||||
} from "../../../client/socketioStore";
|
||||
} from "../../../../client/socketioStore";
|
||||
import { MachineIdentificationUnique } from "@/machines/types";
|
||||
import {
|
||||
createTimeSeries,
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -12,7 +12,7 @@ import { wagoAiTestMachineSerialRoute } from "@/routes/routes";
|
||||
import { produce } from "immer";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import z from "zod";
|
||||
import { wagoAiTestMachine } from "../properties";
|
||||
import { wagoAiTestMachine } from "../../properties";
|
||||
import {
|
||||
StateEvent,
|
||||
useWagoAiTestMachineNamespace,
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -43,44 +43,44 @@ import { Laser1GraphsPage } from "@/machines/laser/laser1/Laser1Graph";
|
||||
import { Laser1Page } from "@/machines/laser/laser1/Laser1Page";
|
||||
import { Laser1PresetsPage } from "@/machines/laser/laser1/Laser1PresetsPage";
|
||||
|
||||
import { Mock1ControlPage } from "@/machines/mock/mock1/Mock1ControlPage";
|
||||
import { Mock1GraphPage } from "@/machines/mock/mock1/Mock1Graph";
|
||||
import { Mock1ManualPage } from "@/machines/mock/mock1/Mock1Manual";
|
||||
import { Mock1Page } from "@/machines/mock/mock1/Mock1Page";
|
||||
import { Mock1PresetsPage } from "@/machines/mock/mock1/Mock1PresetsPage";
|
||||
import { Mock1ControlPage } from "@/machines/minimal_machines/mock/mock1/Mock1ControlPage";
|
||||
import { Mock1GraphPage } from "@/machines/minimal_machines/mock/mock1/Mock1Graph";
|
||||
import { Mock1ManualPage } from "@/machines/minimal_machines/mock/mock1/Mock1Manual";
|
||||
import { Mock1Page } from "@/machines/minimal_machines/mock/mock1/Mock1Page";
|
||||
import { Mock1PresetsPage } from "@/machines/minimal_machines/mock/mock1/Mock1PresetsPage";
|
||||
|
||||
import { Aquapath1ControlPage } from "@/machines/aquapath/aquapath1/Aquapath1ControlPage";
|
||||
import { Aquapath1Page } from "@/machines/aquapath/aquapath1/Aquapath1Page";
|
||||
import { Aquapath1GraphPage } from "@/machines/aquapath/aquapath1/Aquapath1Graph";
|
||||
import { Aquapath1SettingsPage } from "@/machines/aquapath/aquapath1/Aquapath1Settings";
|
||||
|
||||
import { TestMachinePage } from "@/machines/testmachine/TestMachinePage";
|
||||
import { TestMachineControlPage } from "@/machines/testmachine/TestMachineControlPage";
|
||||
import { TestMachinePage } from "@/machines/minimal_machines/testmachine/TestMachinePage";
|
||||
import { TestMachineControlPage } from "@/machines/minimal_machines/testmachine/TestMachineControlPage";
|
||||
|
||||
import { TestMachineStepperPage } from "@/machines/testmachinestepper/TestMachineStepperPage";
|
||||
import { TestMachineStepperControlPage } from "@/machines/testmachinestepper/TestMachineStepperControlPage";
|
||||
import { AnalogInputTestMachine } from "@/machines/analoginputtestmachine/AnalogInputTestMachinePage";
|
||||
import { AnalogInputTestMachineControl } from "@/machines/analoginputtestmachine/AnalogInputTestMachineControlPage";
|
||||
import { WagoAiTestMachine } from "@/machines/wagoaitestmachine/WagoAiTestMachinePage";
|
||||
import { WagoAiTestMachineControl } from "@/machines/wagoaitestmachine/WagoAiTestMachineControlPage";
|
||||
import { TestMachineStepperPage } from "@/machines/minimal_machines/testmachinestepper/TestMachineStepperPage";
|
||||
import { TestMachineStepperControlPage } from "@/machines/minimal_machines/testmachinestepper/TestMachineStepperControlPage";
|
||||
import { AnalogInputTestMachine } from "@/machines/minimal_machines/analoginputtestmachine/AnalogInputTestMachinePage";
|
||||
import { AnalogInputTestMachineControl } from "@/machines/minimal_machines/analoginputtestmachine/AnalogInputTestMachineControlPage";
|
||||
import { WagoAiTestMachine } from "@/machines/minimal_machines/wagoaitestmachine/WagoAiTestMachinePage";
|
||||
import { WagoAiTestMachineControl } from "@/machines/minimal_machines/wagoaitestmachine/WagoAiTestMachineControlPage";
|
||||
|
||||
import { DigitalInputTestMachinePage } from "@/machines/digitalinputtestmachine/DigitalInputTestMachinePage";
|
||||
import { DigitalInputTestMachineControlPage } from "@/machines/digitalinputtestmachine/DigitalInputTestMachineControlPage";
|
||||
import { DigitalInputTestMachinePage } from "@/machines/minimal_machines/digitalinputtestmachine/DigitalInputTestMachinePage";
|
||||
import { DigitalInputTestMachineControlPage } from "@/machines/minimal_machines/digitalinputtestmachine/DigitalInputTestMachineControlPage";
|
||||
|
||||
import { IP20TestMachinePage } from "@/machines/ip20testmachine/IP20TestMachinePage";
|
||||
import { IP20TestMachineControlPage } from "@/machines/ip20testmachine/IP20TestMachineControlPage";
|
||||
import { TestMotorPage } from "@/machines/motor_test_machine/TestMotorPage";
|
||||
import { TestMotorControlPage } from "@/machines/motor_test_machine/TestMotorControlPage";
|
||||
import { IP20TestMachinePage } from "@/machines/minimal_machines/ip20testmachine/IP20TestMachinePage";
|
||||
import { IP20TestMachineControlPage } from "@/machines/minimal_machines/ip20testmachine/IP20TestMachineControlPage";
|
||||
import { TestMotorPage } from "@/machines/minimal_machines/motor_test_machine/TestMotorPage";
|
||||
import { TestMotorControlPage } from "@/machines/minimal_machines/motor_test_machine/TestMotorControlPage";
|
||||
|
||||
import { MetricsGraphsPage } from "@/metrics/MetricsGraphsPage";
|
||||
import { MetricsControlPage } from "@/metrics/MetricsControlPage";
|
||||
|
||||
import { WagoPower1Page } from "@/machines/wago_power/wago_power1/WagoPower1Page";
|
||||
import { WagoPower1ControlPage } from "@/machines/wago_power/wago_power1/WagoPower1ControlPage";
|
||||
import { WagoDoTestMachinePage } from "@/machines/wagodotestmachine/WagoDoTestMachinePage";
|
||||
import { WagoDoTestMachineControlPage } from "@/machines/wagodotestmachine/WagoDoTestMachineControlPage";
|
||||
import { Wago8chDioTestMachinePage } from "@/machines/wago8chdiotestmachine/wago8chDioTestMachinePage";
|
||||
import { Wago8chDioTestMachineControlRoute } from "@/machines/wago8chdiotestmachine/wago8chDioTestMachineControlPage";
|
||||
import { WagoDoTestMachinePage } from "@/machines/minimal_machines/wagodotestmachine/WagoDoTestMachinePage";
|
||||
import { WagoDoTestMachineControlPage } from "@/machines/minimal_machines/wagodotestmachine/WagoDoTestMachineControlPage";
|
||||
import { Wago8chDioTestMachinePage } from "@/machines/minimal_machines/wago8chdiotestmachine/wago8chDioTestMachinePage";
|
||||
import { Wago8chDioTestMachineControlRoute } from "@/machines/minimal_machines/wago8chdiotestmachine/wago8chDioTestMachineControlPage";
|
||||
|
||||
// make a route tree like this
|
||||
// _mainNavigation/machines/winder2/$serial/control
|
||||
|
||||
Generated
+111
-12
@@ -1,6 +1,21 @@
|
||||
{
|
||||
"nodes": {
|
||||
"crane": {
|
||||
"locked": {
|
||||
"lastModified": 1771796463,
|
||||
"narHash": "sha256-9bCDuUzpwJXcHMQYMS1yNuzYMmKO/CCwCexpjWOl62I=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "3d3de3313e263e04894f284ac18177bd26169bad",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"crane_2": {
|
||||
"locked": {
|
||||
"lastModified": 1750266157,
|
||||
"narHash": "sha256-tL42YoNg9y30u7zAqtoGDNdTyXTi8EALDeCB13FtbQA=",
|
||||
@@ -15,7 +30,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"crane_2": {
|
||||
"crane_3": {
|
||||
"locked": {
|
||||
"lastModified": 1750266157,
|
||||
"narHash": "sha256-tL42YoNg9y30u7zAqtoGDNdTyXTi8EALDeCB13FtbQA=",
|
||||
@@ -102,12 +117,51 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_5": {
|
||||
"inputs": {
|
||||
"systems": "systems_5"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1771851181,
|
||||
"narHash": "sha256-gFgE6mGUftwseV3DUENMb0k0EiHd739lZexPo5O/sdQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "9a4b494b1aa1b93d8edf167f46dc8e0c0011280c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"qitech-control",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1750973805,
|
||||
"narHash": "sha256-BZXgag7I0rnL/HMHAsBz3tQrfKAibpY2vovexl2lS+Y=",
|
||||
@@ -122,9 +176,10 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager_2": {
|
||||
"home-manager_3": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"qitech-control",
|
||||
"qitech-control",
|
||||
"nixpkgs"
|
||||
]
|
||||
@@ -143,9 +198,10 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager_3": {
|
||||
"home-manager_4": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"qitech-control",
|
||||
"qitech-control",
|
||||
"qitech-control",
|
||||
"nixpkgs"
|
||||
@@ -167,11 +223,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1750969886,
|
||||
"narHash": "sha256-zW/OFnotiz/ndPFdebpo3X0CrbVNf22n4DjN2vxlb58=",
|
||||
"lastModified": 1767313136,
|
||||
"narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a676066377a2fe7457369dd37c31fd2263b662f4",
|
||||
"rev": "ac62194c3917d5f474c1a844b6fd6da2db95077d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -207,6 +263,31 @@
|
||||
],
|
||||
"qitech-control": "qitech-control_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1772023797,
|
||||
"narHash": "sha256-RipiUlH5UKiofTRquREluUFOwEfNjLFEvyDol/VWDRg=",
|
||||
"owner": "qitechgmbh",
|
||||
"repo": "control",
|
||||
"rev": "3e3e3f1c0bc2b26fe0fc1e7d49b65e3fd3d7f368",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "qitechgmbh",
|
||||
"repo": "control",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"qitech-control_2": {
|
||||
"inputs": {
|
||||
"crane": "crane_3",
|
||||
"flake-utils": "flake-utils_3",
|
||||
"home-manager": "home-manager_3",
|
||||
"nixpkgs": [
|
||||
"qitech-control",
|
||||
"nixpkgs"
|
||||
],
|
||||
"qitech-control": "qitech-control_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751047510,
|
||||
"narHash": "sha256-SZ+DxQvFJKJOro1AVUwWltLQO1L9TIc6AI4tnM9V4tk=",
|
||||
@@ -221,15 +302,16 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"qitech-control_2": {
|
||||
"qitech-control_3": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_3",
|
||||
"home-manager": "home-manager_3",
|
||||
"flake-utils": "flake-utils_4",
|
||||
"home-manager": "home-manager_4",
|
||||
"nixpkgs": [
|
||||
"qitech-control",
|
||||
"qitech-control",
|
||||
"nixpkgs"
|
||||
],
|
||||
"qitech-control": "qitech-control_3",
|
||||
"qitech-control": "qitech-control_4",
|
||||
"rust-overlay": "rust-overlay_2"
|
||||
},
|
||||
"locked": {
|
||||
@@ -246,10 +328,11 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"qitech-control_3": {
|
||||
"qitech-control_4": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_4",
|
||||
"flake-utils": "flake-utils_5",
|
||||
"nixpkgs": [
|
||||
"qitech-control",
|
||||
"qitech-control",
|
||||
"qitech-control",
|
||||
"nixpkgs"
|
||||
@@ -300,6 +383,7 @@
|
||||
"rust-overlay_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"qitech-control",
|
||||
"qitech-control",
|
||||
"qitech-control",
|
||||
"nixpkgs"
|
||||
@@ -378,6 +462,21 @@
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_5": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "QiTech Control";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
|
||||
|
||||
# Crane for Rust builds with dependency caching
|
||||
crane = { url = "github:ipetkov/crane"; };
|
||||
|
||||
+1
-10
@@ -17,25 +17,16 @@ use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
pub mod analog_input_test_machine;
|
||||
pub mod aquapath1;
|
||||
#[cfg(not(feature = "mock-machine"))]
|
||||
pub mod buffer1;
|
||||
pub mod digital_input_test_machine;
|
||||
pub mod extruder1;
|
||||
pub mod extruder2;
|
||||
pub mod ip20_test_machine;
|
||||
pub mod laser;
|
||||
pub mod machine_identification;
|
||||
pub mod mock;
|
||||
pub mod motor_test_machine;
|
||||
pub mod minimal_machines;
|
||||
pub mod registry;
|
||||
pub mod serial;
|
||||
pub mod test_machine;
|
||||
pub mod test_machine_stepper;
|
||||
pub mod wago_8ch_dio_test_machine;
|
||||
pub mod wago_ai_test_machine;
|
||||
pub mod wago_do_test_machine;
|
||||
pub mod wago_power;
|
||||
pub mod winder2;
|
||||
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::{
|
||||
MachineAct, MachineMessage, MachineValues, analog_input_test_machine::AnalogInputTestMachine,
|
||||
MachineAct, MachineMessage, MachineValues,
|
||||
minimal_machines::analog_input_test_machine::AnalogInputTestMachine,
|
||||
};
|
||||
|
||||
impl MachineAct for AnalogInputTestMachine {
|
||||
+1
-1
@@ -8,7 +8,7 @@ use control_core::socketio::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{MachineApi, analog_input_test_machine::AnalogInputTestMachine};
|
||||
use crate::{MachineApi, minimal_machines::analog_input_test_machine::AnalogInputTestMachine};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnalogInputTestMachineNamespace {
|
||||
+1
-3
@@ -4,11 +4,9 @@ use control_core::socketio::{event::Event, namespace::NamespaceCacheingLogic};
|
||||
use ethercat_hal::io::analog_input::AnalogInput;
|
||||
use smol::channel::{Receiver, Sender};
|
||||
|
||||
use self::api::{AnalogInputTestMachineEvents, AnalogInputTestMachineNamespace, MeasurementEvent};
|
||||
use crate::{
|
||||
ANALOG_INPUT_TEST_MACHINE, AsyncThreadMessage, Machine, MachineMessage, VENDOR_QITECH,
|
||||
analog_input_test_machine::api::{
|
||||
AnalogInputTestMachineEvents, AnalogInputTestMachineNamespace, MeasurementEvent,
|
||||
},
|
||||
machine_identification::{MachineIdentification, MachineIdentificationUnique},
|
||||
};
|
||||
|
||||
+3
-3
@@ -7,10 +7,10 @@ use ethercat_hal::{
|
||||
};
|
||||
use smol::{block_on, channel::unbounded};
|
||||
|
||||
use super::{AnalogInputTestMachine, api::AnalogInputTestMachineNamespace};
|
||||
use crate::{
|
||||
MachineNewHardware, MachineNewParams, MachineNewTrait,
|
||||
analog_input_test_machine::{AnalogInputTestMachine, api::AnalogInputTestMachineNamespace},
|
||||
get_ethercat_device, validate_no_role_dublicates, validate_same_machine_identification_unique,
|
||||
MachineNewHardware, MachineNewParams, MachineNewTrait, get_ethercat_device,
|
||||
validate_no_role_dublicates, validate_same_machine_identification_unique,
|
||||
};
|
||||
|
||||
impl MachineNewTrait for AnalogInputTestMachine {
|
||||
File renamed without changes.
+1
-1
@@ -8,7 +8,7 @@ use control_core::socketio::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{MachineApi, digital_input_test_machine::DigitalInputTestMachine};
|
||||
use crate::{MachineApi, minimal_machines::digital_input_test_machine::DigitalInputTestMachine};
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
pub struct StateEvent {
|
||||
+1
-3
@@ -4,11 +4,9 @@ use control_core::socketio::namespace::NamespaceCacheingLogic;
|
||||
use ethercat_hal::io::digital_input::DigitalInput;
|
||||
use smol::channel::{Receiver, Sender};
|
||||
|
||||
use self::api::{DigitalInputTestMachineEvents, DigitalInputTestMachineNamespace, StateEvent};
|
||||
use crate::{
|
||||
AsyncThreadMessage, DIGITAL_INPUT_TEST_MACHINE, Machine, MachineMessage, VENDOR_QITECH,
|
||||
digital_input_test_machine::api::{
|
||||
DigitalInputTestMachineEvents, DigitalInputTestMachineNamespace, StateEvent,
|
||||
},
|
||||
machine_identification::{MachineIdentification, MachineIdentificationUnique},
|
||||
};
|
||||
|
||||
+3
-3
@@ -11,10 +11,10 @@ use ethercat_hal::{
|
||||
};
|
||||
use smol::{block_on, lock::RwLock};
|
||||
|
||||
use super::{DigitalInputTestMachine, api::DigitalInputTestMachineNamespace};
|
||||
use crate::{
|
||||
MachineNewHardware, MachineNewParams, MachineNewTrait,
|
||||
digital_input_test_machine::{DigitalInputTestMachine, api::DigitalInputTestMachineNamespace},
|
||||
get_ethercat_device, validate_no_role_dublicates, validate_same_machine_identification_unique,
|
||||
MachineNewHardware, MachineNewParams, MachineNewTrait, get_ethercat_device,
|
||||
validate_no_role_dublicates, validate_same_machine_identification_unique,
|
||||
};
|
||||
|
||||
impl MachineNewTrait for DigitalInputTestMachine {
|
||||
File renamed without changes.
File renamed without changes.
+4
-2
@@ -1,5 +1,7 @@
|
||||
use crate::ip20_test_machine::api::{IP20TestMachineEvents, LiveValuesEvent, StateEvent};
|
||||
use crate::machine_identification::{MachineIdentification, MachineIdentificationUnique};
|
||||
use crate::minimal_machines::ip20_test_machine::api::{
|
||||
IP20TestMachineEvents, LiveValuesEvent, StateEvent,
|
||||
};
|
||||
use crate::{AsyncThreadMessage, Machine, MachineMessage};
|
||||
use control_core::socketio::namespace::NamespaceCacheingLogic;
|
||||
use ethercat_hal::io::digital_input::DigitalInput;
|
||||
@@ -9,7 +11,7 @@ use std::time::Instant;
|
||||
pub mod act;
|
||||
pub mod api;
|
||||
pub mod new;
|
||||
use crate::ip20_test_machine::api::IP20TestMachineNamespace;
|
||||
use crate::minimal_machines::ip20_test_machine::api::IP20TestMachineNamespace;
|
||||
use crate::{IP20_TEST_MACHINE, VENDOR_QITECH};
|
||||
|
||||
#[derive(Debug)]
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::ip20_test_machine::IP20TestMachine;
|
||||
use crate::ip20_test_machine::api::IP20TestMachineNamespace;
|
||||
use crate::minimal_machines::ip20_test_machine::IP20TestMachine;
|
||||
use crate::minimal_machines::ip20_test_machine::api::IP20TestMachineNamespace;
|
||||
use smol::block_on;
|
||||
use std::time::Instant;
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,10 @@
|
||||
pub mod analog_input_test_machine;
|
||||
pub mod digital_input_test_machine;
|
||||
pub mod ip20_test_machine;
|
||||
pub mod mock;
|
||||
pub mod motor_test_machine;
|
||||
pub mod test_machine;
|
||||
pub mod test_machine_stepper;
|
||||
pub mod wago_8ch_dio_test_machine;
|
||||
pub mod wago_ai_test_machine;
|
||||
pub mod wago_do_test_machine;
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::machine_identification::{MachineIdentification, MachineIdentificationUnique};
|
||||
use crate::test_machine::api::{StateEvent, TestMachineEvents};
|
||||
use crate::minimal_machines::test_machine::api::{StateEvent, TestMachineEvents};
|
||||
use crate::{AsyncThreadMessage, Machine, MachineMessage};
|
||||
use control_core::socketio::namespace::NamespaceCacheingLogic;
|
||||
use ethercat_hal::io::digital_output::DigitalOutput;
|
||||
@@ -8,7 +8,7 @@ use std::time::Instant;
|
||||
pub mod act;
|
||||
pub mod api;
|
||||
pub mod new;
|
||||
use crate::test_machine::api::TestMachineNamespace;
|
||||
use crate::minimal_machines::test_machine::api::TestMachineNamespace;
|
||||
use crate::{TEST_MACHINE, VENDOR_QITECH};
|
||||
|
||||
#[derive(Debug)]
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::test_machine::TestMachine;
|
||||
use crate::test_machine::api::TestMachineNamespace;
|
||||
use crate::minimal_machines::test_machine::TestMachine;
|
||||
use crate::minimal_machines::test_machine::api::TestMachineNamespace;
|
||||
use smol::block_on;
|
||||
use std::time::Instant;
|
||||
|
||||
File renamed without changes.
File renamed without changes.
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::machine_identification::{MachineIdentification, MachineIdentificationUnique};
|
||||
use crate::test_machine_stepper::api::{StateEvent, TestMachineStepperEvents};
|
||||
use crate::minimal_machines::test_machine_stepper::api::{StateEvent, TestMachineStepperEvents};
|
||||
use crate::{AsyncThreadMessage, Machine, MachineMessage};
|
||||
use control_core::socketio::namespace::NamespaceCacheingLogic;
|
||||
use ethercat_hal::io::stepper_velocity_wago_750_672::StepperVelocityWago750672;
|
||||
@@ -8,7 +8,7 @@ use std::time::Instant;
|
||||
pub mod act;
|
||||
pub mod api;
|
||||
pub mod new;
|
||||
use crate::test_machine_stepper::api::TestMachineStepperNamespace;
|
||||
use crate::minimal_machines::test_machine_stepper::api::TestMachineStepperNamespace;
|
||||
use crate::{TEST_MACHINE_STEPPER, VENDOR_QITECH};
|
||||
|
||||
#[derive(Debug)]
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
use crate::test_machine_stepper::{TestMachineStepper, api::TestMachineStepperNamespace};
|
||||
use crate::minimal_machines::test_machine_stepper::{
|
||||
TestMachineStepper, api::TestMachineStepperNamespace,
|
||||
};
|
||||
use ethercat_hal::{
|
||||
devices::{
|
||||
EthercatDevice, downcast_device,
|
||||
File renamed without changes.
+1
-1
@@ -8,7 +8,7 @@ use control_core::socketio::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{MachineApi, wago_8ch_dio_test_machine::Wago8chDigitalIOTestMachine};
|
||||
use crate::{MachineApi, minimal_machines::wago_8ch_dio_test_machine::Wago8chDigitalIOTestMachine};
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
pub struct StateEvent {
|
||||
+3
-3
@@ -4,12 +4,12 @@ use control_core::socketio::namespace::NamespaceCacheingLogic;
|
||||
use ethercat_hal::io::{digital_input::DigitalInput, digital_output::DigitalOutput};
|
||||
use smol::channel::{Receiver, Sender};
|
||||
|
||||
use self::api::{
|
||||
StateEvent, Wago8chDigitalIOTestMachineEvents, Wago8chDigitalIOTestMachineNamespace,
|
||||
};
|
||||
use crate::{
|
||||
AsyncThreadMessage, Machine, MachineMessage, VENDOR_QITECH, WAGO_8CH_IO_TEST_MACHINE,
|
||||
machine_identification::{MachineIdentification, MachineIdentificationUnique},
|
||||
wago_8ch_dio_test_machine::api::{
|
||||
StateEvent, Wago8chDigitalIOTestMachineEvents, Wago8chDigitalIOTestMachineNamespace,
|
||||
},
|
||||
};
|
||||
|
||||
pub mod act;
|
||||
+1
-3
@@ -13,12 +13,10 @@ use ethercat_hal::{
|
||||
};
|
||||
use smol::{block_on, lock::RwLock};
|
||||
|
||||
use super::{Wago8chDigitalIOTestMachine, api::Wago8chDigitalIOTestMachineNamespace};
|
||||
use crate::{
|
||||
MachineNewHardware, MachineNewParams, MachineNewTrait, get_ethercat_device,
|
||||
validate_no_role_dublicates, validate_same_machine_identification_unique,
|
||||
wago_8ch_dio_test_machine::{
|
||||
Wago8chDigitalIOTestMachine, api::Wago8chDigitalIOTestMachineNamespace,
|
||||
},
|
||||
};
|
||||
|
||||
impl MachineNewTrait for Wago8chDigitalIOTestMachine {
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::{MachineAct, MachineMessage, MachineValues, wago_ai_test_machine::WagoAiTestMachine};
|
||||
use crate::{
|
||||
MachineAct, MachineMessage, MachineValues,
|
||||
minimal_machines::wago_ai_test_machine::WagoAiTestMachine,
|
||||
};
|
||||
|
||||
impl MachineAct for WagoAiTestMachine {
|
||||
fn act_machine_message(&mut self, msg: MachineMessage) {
|
||||
+1
-1
@@ -8,7 +8,7 @@ use control_core::socketio::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{MachineApi, wago_ai_test_machine::WagoAiTestMachine};
|
||||
use crate::{MachineApi, minimal_machines::wago_ai_test_machine::WagoAiTestMachine};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WagoAiTestMachineNamespace {
|
||||
+1
-3
@@ -4,12 +4,10 @@ use control_core::socketio::{event::Event, namespace::NamespaceCacheingLogic};
|
||||
use ethercat_hal::io::analog_input::AnalogInput;
|
||||
use smol::channel::{Receiver, Sender};
|
||||
|
||||
use self::api::{AnalogInputsEvent, WagoAiTestMachineEvents, WagoAiTestMachineNamespace};
|
||||
use crate::{
|
||||
AsyncThreadMessage, Machine, MachineMessage, VENDOR_QITECH, WAGO_AI_TEST_MACHINE,
|
||||
machine_identification::{MachineIdentification, MachineIdentificationUnique},
|
||||
wago_ai_test_machine::api::{
|
||||
AnalogInputsEvent, WagoAiTestMachineEvents, WagoAiTestMachineNamespace,
|
||||
},
|
||||
};
|
||||
|
||||
pub mod act;
|
||||
+1
-1
@@ -12,10 +12,10 @@ use ethercat_hal::{
|
||||
use smol::{block_on, channel::unbounded, lock::RwLock};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{WagoAiTestMachine, api::WagoAiTestMachineNamespace};
|
||||
use crate::{
|
||||
MachineNewHardware, MachineNewParams, MachineNewTrait, get_ethercat_device,
|
||||
validate_no_role_dublicates, validate_same_machine_identification_unique,
|
||||
wago_ai_test_machine::{WagoAiTestMachine, api::WagoAiTestMachineNamespace},
|
||||
};
|
||||
|
||||
impl MachineNewTrait for WagoAiTestMachine {
|
||||
File renamed without changes.
File renamed without changes.
+1
-1
@@ -4,10 +4,10 @@ use control_core::socketio::namespace::NamespaceCacheingLogic;
|
||||
use ethercat_hal::io::digital_output::DigitalOutput;
|
||||
use smol::channel::{Receiver, Sender};
|
||||
|
||||
use self::api::{StateEvent, WagoDOTestMachineEvents, WagoDOTestMachineNamespace};
|
||||
use crate::{
|
||||
AsyncThreadMessage, Machine, MachineMessage, VENDOR_QITECH, WAGO_DO_TEST_MACHINE,
|
||||
machine_identification::{MachineIdentification, MachineIdentificationUnique},
|
||||
wago_do_test_machine::api::{StateEvent, WagoDOTestMachineEvents, WagoDOTestMachineNamespace},
|
||||
};
|
||||
|
||||
pub mod act;
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::wago_do_test_machine::WagoDOTestMachine;
|
||||
use crate::wago_do_test_machine::api::WagoDOTestMachineNamespace;
|
||||
use crate::minimal_machines::wago_do_test_machine::WagoDOTestMachine;
|
||||
use crate::minimal_machines::wago_do_test_machine::api::WagoDOTestMachineNamespace;
|
||||
use smol::block_on;
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::analog_input_test_machine::AnalogInputTestMachine;
|
||||
use crate::digital_input_test_machine::DigitalInputTestMachine;
|
||||
use crate::ip20_test_machine::IP20TestMachine;
|
||||
use crate::wago_8ch_dio_test_machine::Wago8chDigitalIOTestMachine;
|
||||
use crate::wago_ai_test_machine::WagoAiTestMachine;
|
||||
use crate::wago_do_test_machine::WagoDOTestMachine;
|
||||
use crate::minimal_machines::analog_input_test_machine::AnalogInputTestMachine;
|
||||
use crate::minimal_machines::digital_input_test_machine::DigitalInputTestMachine;
|
||||
use crate::minimal_machines::ip20_test_machine::IP20TestMachine;
|
||||
use crate::minimal_machines::wago_8ch_dio_test_machine::Wago8chDigitalIOTestMachine;
|
||||
use crate::minimal_machines::wago_ai_test_machine::WagoAiTestMachine;
|
||||
use crate::minimal_machines::wago_do_test_machine::WagoDOTestMachine;
|
||||
#[cfg(feature = "mock-machine")]
|
||||
use crate::{
|
||||
extruder1::mock::ExtruderV2 as ExtruderV2Mock1, extruder2::mock::ExtruderV2 as ExtruderV2Mock2,
|
||||
mock::MockMachine, winder2::mock::Winder2,
|
||||
minimal_machines::mock::MockMachine, winder2::mock::Winder2,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -22,11 +22,13 @@ use crate::{
|
||||
winder2::Winder2,
|
||||
};
|
||||
|
||||
use crate::{test_machine::TestMachine, test_machine_stepper::TestMachineStepper};
|
||||
use crate::minimal_machines::{
|
||||
test_machine::TestMachine, test_machine_stepper::TestMachineStepper,
|
||||
};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::motor_test_machine::MotorTestMachine;
|
||||
use crate::minimal_machines::motor_test_machine::MotorTestMachine;
|
||||
use anyhow::Error;
|
||||
use std::{any::TypeId, collections::HashMap};
|
||||
|
||||
|
||||
+16
-10
@@ -15,7 +15,7 @@ in {
|
||||
consoleMode = "max"; # Use the highest available resolution
|
||||
};
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
boot.kernelPackages = pkgs.linuxPackages_6_13;
|
||||
boot.kernelPackages = pkgs.linuxPackages_6_18;
|
||||
boot.kernelModules = [ "i915" ];
|
||||
|
||||
boot.kernelParams = [
|
||||
@@ -134,7 +134,6 @@ in {
|
||||
|
||||
# Enable the X11 windowing system.
|
||||
services.xserver.enable = true;
|
||||
#services.xserver.videoDrivers = [ "intel" ];
|
||||
services.xserver.displayManager.gdm = {
|
||||
enable = true;
|
||||
autoSuspend = false;
|
||||
@@ -186,18 +185,25 @@ in {
|
||||
powertop.enable = false;
|
||||
};
|
||||
|
||||
# Ensure all power management is disabled
|
||||
|
||||
services.logind = {
|
||||
# This remains for backward compatibility/high-level override
|
||||
lidSwitch = "ignore";
|
||||
extraConfig = ''
|
||||
HandlePowerKey=ignore
|
||||
HandleSuspendKey=ignore
|
||||
HandleHibernateKey=ignore
|
||||
HandleLidSwitch=ignore
|
||||
IdleAction=ignore
|
||||
'';
|
||||
|
||||
# Structured settings for logind.conf
|
||||
settings = {
|
||||
Login = {
|
||||
HandlePowerKey = "ignore";
|
||||
HandleSuspendKey = "ignore";
|
||||
HandleHibernateKey = "ignore";
|
||||
HandleLidSwitch = "ignore";
|
||||
IdleAction = "ignore";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
# Configure keymap in X11
|
||||
services.xserver.xkb = {
|
||||
layout = "de";
|
||||
|
||||
@@ -10,7 +10,7 @@ buildNpmPackage rec {
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD = 1;
|
||||
|
||||
makeCacheWritable = true;
|
||||
npmDepsHash = "sha256-N2kdS9IKkEMNOPPnRNQ9GvCenR/k88fVyAFzXMJyxs4=";
|
||||
npmDepsHash = "sha256-jW9nogboiuYBzjLNM5rlYsdfp+58OlDf+kxjgNlST70=";
|
||||
npmFlags = [ "--no-audit" "--no-fund" ];
|
||||
|
||||
installPhase = ''
|
||||
@@ -44,7 +44,6 @@ buildNpmPackage rec {
|
||||
--enable-gpu \
|
||||
--ignore-gpu-blocklist \
|
||||
--enable-gpu-rasterization \
|
||||
--disable-software-rasterizer \
|
||||
"$@"
|
||||
EOF
|
||||
chmod +x $out/bin/qitech-control-electron
|
||||
|
||||
@@ -30,6 +30,9 @@ docker run --rm \
|
||||
--workdir /workspace \
|
||||
nixos/nix:latest \
|
||||
sh -c "
|
||||
whoami
|
||||
ls -lh
|
||||
cd /workspace
|
||||
echo 'Enabling flakes and nix-command...'
|
||||
mkdir -p ~/.config/nix
|
||||
echo 'experimental-features = nix-command flakes' > ~/.config/nix/nix.conf
|
||||
|
||||
Loaded 100 of 103 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user