@adcops/autocore-react 3.3.73 → 3.3.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/assets/HomeMotor.d.ts +4 -0
  2. package/dist/assets/HomeMotor.d.ts.map +1 -0
  3. package/dist/assets/HomeMotor.js +1 -0
  4. package/dist/assets/svg/home_motor.svg +57 -0
  5. package/dist/components/Indicator.d.ts +29 -52
  6. package/dist/components/Indicator.d.ts.map +1 -1
  7. package/dist/components/Indicator.js +1 -1
  8. package/dist/components/ValueInput.d.ts +1 -1
  9. package/dist/components/ValueInput.d.ts.map +1 -1
  10. package/dist/components/ams/AmsProvider.d.ts +7 -0
  11. package/dist/components/ams/AmsProvider.d.ts.map +1 -1
  12. package/dist/components/ams/AssetDetailView.d.ts.map +1 -1
  13. package/dist/components/ams/AssetDetailView.js +1 -1
  14. package/dist/components/ams/AssetRegistryTable.d.ts.map +1 -1
  15. package/dist/components/ams/AssetRegistryTable.js +1 -1
  16. package/dist/components/ams/CalibrationEntryDialog.d.ts.map +1 -1
  17. package/dist/components/ams/CalibrationEntryDialog.js +1 -1
  18. package/dist/components/ams/MissingAssetsBanner.d.ts +11 -0
  19. package/dist/components/ams/MissingAssetsBanner.d.ts.map +1 -0
  20. package/dist/components/ams/MissingAssetsBanner.js +1 -0
  21. package/dist/components/ams/PlaceholderHealthPanel.d.ts +3 -0
  22. package/dist/components/ams/PlaceholderHealthPanel.d.ts.map +1 -0
  23. package/dist/components/ams/PlaceholderHealthPanel.js +1 -0
  24. package/dist/components/ams/index.d.ts +2 -0
  25. package/dist/components/ams/index.d.ts.map +1 -1
  26. package/dist/components/ams/index.js +1 -1
  27. package/dist/components/index.d.ts +8 -0
  28. package/dist/components/index.d.ts.map +1 -1
  29. package/dist/components/index.js +1 -1
  30. package/dist/components/network/NetworkPanel.d.ts +8 -0
  31. package/dist/components/network/NetworkPanel.d.ts.map +1 -0
  32. package/dist/components/network/NetworkPanel.js +1 -0
  33. package/dist/components/network/NetworkProvider.d.ts +72 -0
  34. package/dist/components/network/NetworkProvider.d.ts.map +1 -0
  35. package/dist/components/network/NetworkProvider.js +1 -0
  36. package/dist/components/network/StagedChangeBanner.d.ts +8 -0
  37. package/dist/components/network/StagedChangeBanner.d.ts.map +1 -0
  38. package/dist/components/network/StagedChangeBanner.js +1 -0
  39. package/dist/components/network/index.d.ts +7 -0
  40. package/dist/components/network/index.d.ts.map +1 -0
  41. package/dist/components/network/index.js +1 -0
  42. package/dist/components/tis/ProjectManager.d.ts +7 -0
  43. package/dist/components/tis/ProjectManager.d.ts.map +1 -0
  44. package/dist/components/tis/ProjectManager.js +1 -0
  45. package/dist/components/tis/ResultHistoryTable.d.ts.map +1 -1
  46. package/dist/components/tis/ResultHistoryTable.js +1 -1
  47. package/dist/components/tis/TestDataView.d.ts.map +1 -1
  48. package/dist/components/tis/TestDataView.js +1 -1
  49. package/dist/components/tis/TestRawDataView.d.ts.map +1 -1
  50. package/dist/components/tis/TestRawDataView.js +1 -1
  51. package/dist/components/tis/TestSetupForm.d.ts +7 -0
  52. package/dist/components/tis/TestSetupForm.d.ts.map +1 -1
  53. package/dist/components/tis/TestSetupForm.js +1 -1
  54. package/package.json +5 -1
  55. package/src/assets/HomeMotor.tsx +37 -0
  56. package/src/assets/svg/home_motor.svg +57 -0
  57. package/src/components/Indicator.tsx +166 -162
  58. package/src/components/ValueInput.tsx +2 -2
  59. package/src/components/ams/AmsProvider.tsx +7 -0
  60. package/src/components/ams/AssetDetailView.tsx +287 -4
  61. package/src/components/ams/AssetRegistryTable.tsx +325 -21
  62. package/src/components/ams/CalibrationEntryDialog.tsx +163 -30
  63. package/src/components/ams/MissingAssetsBanner.tsx +124 -0
  64. package/src/components/ams/PlaceholderHealthPanel.tsx +188 -0
  65. package/src/components/ams/index.ts +2 -0
  66. package/src/components/index.ts +26 -0
  67. package/src/components/network/NetworkPanel.tsx +363 -0
  68. package/src/components/network/NetworkProvider.tsx +349 -0
  69. package/src/components/network/StagedChangeBanner.tsx +101 -0
  70. package/src/components/network/index.ts +17 -0
  71. package/src/components/tis/ProjectManager.tsx +392 -0
  72. package/src/components/tis/ResultHistoryTable.tsx +125 -74
  73. package/src/components/tis/TestDataView.tsx +160 -14
  74. package/src/components/tis/TestRawDataView.tsx +118 -8
  75. package/src/components/tis/TestSetupForm.tsx +42 -1
@@ -0,0 +1,124 @@
1
+ /*
2
+ * <MissingAssetsBanner> — warning panel for unfilled asset roles.
3
+ *
4
+ * Lists every `by_location` asset_ref declared in project.json that
5
+ * has no active asset registered at its location. Each row gets a
6
+ * "Register" button that fires a custom DOM event the
7
+ * <AssetRegistryTable> picks up to open its Add dialog pre-populated
8
+ * with the asset_type + location.
9
+ *
10
+ * Zero-prop. Hides itself when zero missing. Drop above
11
+ * <AssetRegistryTable> in the AIS tab.
12
+ */
13
+
14
+ import React, { useMemo } from 'react';
15
+ import { Button } from 'primereact/button';
16
+ import { useAms, type AmsRole } from './AmsProvider';
17
+
18
+ /** Custom event the banner emits; <AssetRegistryTable> listens for it
19
+ * and opens its Add dialog pre-populated. Decouples the two so the
20
+ * banner can sit anywhere relative to the table in the layout. */
21
+ export const PREFILL_ADD_EVENT = 'ams:prefill-add';
22
+ export interface PrefillAddDetail {
23
+ assetType: string;
24
+ location: string;
25
+ }
26
+
27
+ export const MissingAssetsBanner: React.FC = () => {
28
+ const { roles, assets, rolesLoaded } = useAms();
29
+
30
+ /** Flatten the role registry into (asset_type, AmsRole) tuples.
31
+ * Skip roles that have at least one active asset registered —
32
+ * those are filled and don't need attention. Also skip roles
33
+ * marked `by_id_field`-only (those have no fixed slot — they're
34
+ * picked per-test by the operator). */
35
+ const missing: Array<{ assetType: string; role: AmsRole }> = useMemo(() => {
36
+ const result: Array<{ assetType: string; role: AmsRole }> = [];
37
+ for (const [assetType, list] of Object.entries(roles)) {
38
+ for (const role of list) {
39
+ // Is there an active asset of this type at this location?
40
+ const filled = assets.some(a =>
41
+ a.asset_type === assetType
42
+ && a.location === role.location
43
+ && a.status === 'active'
44
+ );
45
+ if (!filled) {
46
+ result.push({ assetType, role });
47
+ }
48
+ }
49
+ }
50
+ return result;
51
+ }, [roles, assets]);
52
+
53
+ if (!rolesLoaded) return null;
54
+ if (missing.length === 0) return null;
55
+
56
+ const fireRegister = (assetType: string, location: string) => {
57
+ const detail: PrefillAddDetail = { assetType, location };
58
+ window.dispatchEvent(new CustomEvent(PREFILL_ADD_EVENT, { detail }));
59
+ };
60
+
61
+ const oneMany = (n: number, sing: string, plur: string) =>
62
+ n === 1 ? sing : plur;
63
+
64
+ return (
65
+ <div
66
+ role="alert"
67
+ style={{
68
+ borderLeft: '4px solid #f59e0b',
69
+ background: 'rgba(245, 158, 11, 0.08)',
70
+ padding: '0.75rem 1rem',
71
+ marginBottom: '1rem',
72
+ borderRadius: '4px',
73
+ }}
74
+ >
75
+ <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.5rem' }}>
76
+ <i className="pi pi-exclamation-triangle" style={{ color: '#f59e0b' }} />
77
+ <strong>
78
+ {missing.length} {oneMany(missing.length, 'role has', 'roles have')} no active asset registered.
79
+ </strong>
80
+ <span style={{ color: 'var(--text-secondary-color)', fontSize: '0.875rem' }}>
81
+ Modules and tests depending on {oneMany(missing.length, 'it', 'them')} will refuse to start.
82
+ </span>
83
+ </div>
84
+ <ul style={{ margin: 0, paddingLeft: '1.5rem' }}>
85
+ {missing.map(({ assetType, role }) => {
86
+ const label = role.label ?? role.location;
87
+ const ref_summary = [
88
+ role.used_by.length > 0
89
+ ? `methods: ${role.used_by.join(', ')}`
90
+ : null,
91
+ role.used_by_modules.length > 0
92
+ ? `modules: ${role.used_by_modules.join(', ')}`
93
+ : null,
94
+ ].filter(Boolean).join(' • ');
95
+ return (
96
+ <li key={`${assetType}/${role.location}`}
97
+ style={{ marginBottom: '0.25rem',
98
+ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
99
+ <span>
100
+ <strong>{label}</strong>
101
+ <span style={{ color: 'var(--text-secondary-color)' }}>
102
+ {' '}({assetType} at <code>{role.location}</code>)
103
+ </span>
104
+ {ref_summary && (
105
+ <span style={{ color: 'var(--text-secondary-color)',
106
+ fontSize: '0.85em', marginLeft: '0.5rem' }}>
107
+ — {ref_summary}
108
+ </span>
109
+ )}
110
+ </span>
111
+ <Button
112
+ label="Register"
113
+ icon="pi pi-plus"
114
+ size="small"
115
+ outlined
116
+ onClick={() => fireRegister(assetType, role.location)}
117
+ />
118
+ </li>
119
+ );
120
+ })}
121
+ </ul>
122
+ </div>
123
+ );
124
+ };
@@ -0,0 +1,188 @@
1
+ /*
2
+ * <PlaceholderHealthPanel> — pre-flight check for AMS placeholder
3
+ * resolution. Calls `ams.diagnose_placeholders` and lists every
4
+ * `${ams.by_location.*}` reference in every enabled module's config
5
+ * along with its resolution status. Green check = will resolve at
6
+ * module-spawn time. Red ✗ = will block the spawn, with the typed
7
+ * reason ("no active asset registered at location `tsdr_fx`") next to
8
+ * the offending config path.
9
+ *
10
+ * Drop into the AIS tab. Zero-prop: reads from <AmsProvider> context
11
+ * and the EventEmitter for IPC.
12
+ */
13
+
14
+ import React, { useCallback, useContext, useEffect, useState } from 'react';
15
+ import { Button } from 'primereact/button';
16
+ import { DataTable } from 'primereact/datatable';
17
+ import { Column } from 'primereact/column';
18
+ import { EventEmitterContext } from '../../core/EventEmitterContext';
19
+ import { MessageType } from '../../hub/CommandMessage';
20
+ import { useAms } from './AmsProvider';
21
+
22
+ interface PlaceholderRow {
23
+ module: string;
24
+ path: string;
25
+ placeholder: string;
26
+ location: string;
27
+ field_path: string[];
28
+ status: 'ok' | 'unresolved';
29
+ /** Present on `status: ok` rows. */
30
+ asset_id?: string;
31
+ asset_type?: string;
32
+ /** Present on `status: ok` rows — the rendered value the module
33
+ * will receive (number / string / bool depending on the field). */
34
+ resolved_value?: any;
35
+ /** Present on `status: unresolved` rows — human-readable cause. */
36
+ reason?: string;
37
+ }
38
+
39
+ interface Summary {
40
+ total: number;
41
+ ok: number;
42
+ unresolved: number;
43
+ }
44
+
45
+ export const PlaceholderHealthPanel: React.FC = () => {
46
+ const { invoke } = useContext(EventEmitterContext);
47
+ // The panel watches AMS state mutations so refresh is automatic on
48
+ // create/update — the operator shouldn't have to click Refresh
49
+ // after registering the missing asset.
50
+ const { assets, refreshAssets } = useAms();
51
+ const [rows, setRows] = useState<PlaceholderRow[] | null>(null);
52
+ const [summary, setSummary] = useState<Summary | null>(null);
53
+ const [loading, setLoading] = useState(false);
54
+ const [error, setError] = useState<string | null>(null);
55
+
56
+ const refresh = useCallback(async () => {
57
+ setLoading(true);
58
+ setError(null);
59
+ try {
60
+ const resp: any = await invoke(
61
+ 'ams.diagnose_placeholders' as any, MessageType.Request, {} as any,
62
+ );
63
+ if (resp?.success) {
64
+ setRows((resp.data?.placeholders ?? []) as PlaceholderRow[]);
65
+ setSummary(resp.data?.summary ?? null);
66
+ } else {
67
+ setError(resp?.error_message ?? 'diagnose_placeholders failed');
68
+ }
69
+ } catch (e: any) {
70
+ setError(String(e?.message ?? e));
71
+ } finally {
72
+ setLoading(false);
73
+ }
74
+ }, [invoke]);
75
+
76
+ // Initial load and re-load whenever the asset registry changes.
77
+ // `assets` is updated by AmsProvider in response to ams.* broadcasts,
78
+ // so registering a missing load_cell flips its row from red to
79
+ // green without manual interaction.
80
+ useEffect(() => { void refresh(); }, [refresh, assets]);
81
+
82
+ const renderStatus = (row: PlaceholderRow) => {
83
+ if (row.status === 'ok') {
84
+ return (
85
+ <span style={{ color: '#34d399', display: 'inline-flex', alignItems: 'center', gap: '0.25rem' }}>
86
+ <i className="pi pi-check-circle" />
87
+ </span>
88
+ );
89
+ }
90
+ return (
91
+ <span style={{ color: '#f59e0b', display: 'inline-flex', alignItems: 'center', gap: '0.25rem' }}>
92
+ <i className="pi pi-exclamation-triangle" />
93
+ </span>
94
+ );
95
+ };
96
+
97
+ const renderValueOrReason = (row: PlaceholderRow) => {
98
+ if (row.status === 'unresolved') {
99
+ return (
100
+ <span style={{ color: '#f59e0b' }}>{row.reason ?? 'unresolved'}</span>
101
+ );
102
+ }
103
+ // Render the resolved value compactly. Numbers render bare,
104
+ // strings get quotes so the operator can tell "5000" (number)
105
+ // from "5000" (string) — relevant for EtherCAT SDO hex values
106
+ // which are intentionally strings.
107
+ const v = row.resolved_value;
108
+ let body: React.ReactNode;
109
+ if (typeof v === 'string') body = <code>"{v}"</code>;
110
+ else if (typeof v === 'number') body = <code>{v}</code>;
111
+ else if (v === null || v === undefined) body = <em style={{ color: '#9ca3af' }}>null</em>;
112
+ else body = <code>{JSON.stringify(v)}</code>;
113
+ return body;
114
+ };
115
+
116
+ const renderAsset = (row: PlaceholderRow) => {
117
+ if (row.status !== 'ok' || !row.asset_id) {
118
+ return <span style={{ color: '#9ca3af' }}>—</span>;
119
+ }
120
+ return <code style={{ fontSize: '0.85em' }}>{row.asset_id}</code>;
121
+ };
122
+
123
+ const summaryLine = summary && (
124
+ <span>
125
+ {summary.total === 0 ? (
126
+ <span style={{ color: '#9ca3af' }}>
127
+ No <code>{'${ams.*}'}</code> placeholders in any enabled module's config.
128
+ </span>
129
+ ) : summary.unresolved === 0 ? (
130
+ <span style={{ color: '#34d399' }}>
131
+ ✓ All {summary.total} placeholder{summary.total === 1 ? '' : 's'} resolved.
132
+ Modules will start cleanly.
133
+ </span>
134
+ ) : (
135
+ <span style={{ color: '#f59e0b' }}>
136
+ {summary.unresolved} of {summary.total} placeholder
137
+ {summary.total === 1 ? '' : 's'} can't be resolved.
138
+ Register the missing asset{summary.unresolved === 1 ? '' : 's'} before
139
+ starting the affected module{summary.unresolved === 1 ? '' : 's'}.
140
+ </span>
141
+ )}
142
+ </span>
143
+ );
144
+
145
+ return (
146
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
147
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
148
+ <h4 style={{ margin: 0 }}>Module Placeholder Health</h4>
149
+ <Button
150
+ icon="pi pi-refresh"
151
+ label="Refresh"
152
+ onClick={() => { void refreshAssets(); void refresh(); }}
153
+ loading={loading}
154
+ severity="secondary"
155
+ outlined
156
+ />
157
+ </div>
158
+
159
+ {error && (
160
+ <div style={{ color: '#ef4444', fontSize: '0.875rem' }}>
161
+ Couldn't fetch placeholder diagnostic: {error}
162
+ </div>
163
+ )}
164
+
165
+ <div style={{ fontSize: '0.875rem' }}>{summaryLine}</div>
166
+
167
+ {rows && rows.length > 0 && (
168
+ <DataTable
169
+ value={rows}
170
+ size="small"
171
+ stripedRows
172
+ sortField="status"
173
+ sortOrder={-1}
174
+ emptyMessage="No placeholders to report."
175
+ >
176
+ <Column header="" body={renderStatus} style={{ width: '2rem' }} />
177
+ <Column field="module" header="Module" style={{ width: '8rem' }} />
178
+ <Column field="location" header="Role" style={{ width: '10rem' }} />
179
+ <Column header="Value / Reason" body={renderValueOrReason} />
180
+ <Column header="Asset" body={renderAsset} style={{ width: '14rem' }} />
181
+ <Column field="path" header="Config path"
182
+ body={(r: PlaceholderRow) => <code style={{ fontSize: '0.8em' }}>{r.path}</code>}
183
+ />
184
+ </DataTable>
185
+ )}
186
+ </div>
187
+ );
188
+ };
@@ -11,3 +11,5 @@ export { AssetRegistryTable } from './AssetRegistryTable';
11
11
  export { AssetDetailView } from './AssetDetailView';
12
12
  export { CalibrationEntryDialog } from './CalibrationEntryDialog';
13
13
  export { SubLocationPicker } from './SubLocationPicker';
14
+ export { PlaceholderHealthPanel } from './PlaceholderHealthPanel';
15
+ export { MissingAssetsBanner } from './MissingAssetsBanner';
@@ -48,6 +48,9 @@ export type { TestDataViewProps, ChartAxis, ChartSeries, ChartView, RawDataShape
48
48
  export { TestRawDataView } from './tis/TestRawDataView';
49
49
  export type { TestRawDataViewProps } from './tis/TestRawDataView';
50
50
 
51
+ export { ProjectManager } from './tis/ProjectManager';
52
+ export type { ProjectManagerProps } from './tis/ProjectManager';
53
+
51
54
  // -----------------------------------------------------------------------
52
55
  // Asset Management System — see autocore-server/doc/ams_product_plan.md
53
56
  // Drop <AmsProvider> at the top of your HMI; the rest are zero-prop.
@@ -74,3 +77,26 @@ export { AssetDetailView } from './ams/AssetDetailView';
74
77
  export { CalibrationEntryDialog } from './ams/CalibrationEntryDialog';
75
78
  export type { CalibrationEntryDialogProps } from './ams/CalibrationEntryDialog';
76
79
  export { SubLocationPicker } from './ams/SubLocationPicker';
80
+
81
+ // -----------------------------------------------------------------------
82
+ // Network management — wraps the autocore-server `nw.*` IPC servelet
83
+ // (nmcli/NetworkManager backend). Drop <NetworkProvider> at the top of
84
+ // your HMI, render <StagedChangeBanner> in a high-z slot so the
85
+ // revert countdown is visible on every tab, and mount <NetworkPanel>
86
+ // on the Network tab.
87
+ // -----------------------------------------------------------------------
88
+ export { NetworkProvider, useNetwork } from './network/NetworkProvider';
89
+ export type {
90
+ NetworkProviderProps,
91
+ NetworkContextValue,
92
+ NetworkStatus,
93
+ NetworkInterface,
94
+ NetworkConnection,
95
+ NetworkDeviceIp4,
96
+ WifiAp,
97
+ StagedChange,
98
+ } from './network/NetworkProvider';
99
+ export { NetworkPanel } from './network/NetworkPanel';
100
+ export type { NetworkPanelProps } from './network/NetworkPanel';
101
+ export { StagedChangeBanner } from './network/StagedChangeBanner';
102
+ export type { StagedChangeBannerProps } from './network/StagedChangeBanner';