@adcops/autocore-react 3.3.9 → 3.3.10
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.
- package/LICENSE +58 -58
- package/additional-docs/AutoCoreTagContext.md +441 -441
- package/additional-docs/ButtonApiSpecs.md +48 -48
- package/additional-docs/GlobalEventEmitter.md +243 -243
- package/additional-docs/general_recommendations.md +22 -22
- package/additional-docs/react_performance_notes.md +94 -94
- package/dist/assets/svg/blockly_logo.svg +82 -82
- package/dist/assets/svg/distance.svg +40 -40
- package/dist/assets/svg/python_logo.svg +246 -246
- package/dist/assets/svg/rotation_ccw.svg +50 -50
- package/dist/assets/svg/rotation_ccw_a.svg +57 -57
- package/dist/assets/svg/rotation_ccw_b.svg +57 -57
- package/dist/assets/svg/rotation_ccw_c.svg +57 -57
- package/dist/assets/svg/rotation_cw.svg +49 -49
- package/dist/assets/svg/rotation_cw_a.svg +30 -30
- package/dist/assets/svg/rotation_cw_b.svg +30 -30
- package/dist/assets/svg/rotation_cw_c.svg +30 -30
- package/dist/assets/svg/speed.svg +39 -39
- package/dist/components/BlocklyEditor.css +93 -93
- package/dist/components/JogPanel.css +41 -41
- package/dist/components/ProgressBarWithValue.css +27 -27
- package/dist/components/ValueIndicator.css +31 -31
- package/dist/components/osk.css +123 -123
- package/dist/core/AutoCoreTagContext.d.ts.map +1 -1
- package/dist/core/AutoCoreTagContext.js +1 -1
- package/dist/hub/HubBase.d.ts +3 -3
- package/dist/hub/HubBase.d.ts.map +1 -1
- package/dist/hub/HubBase.js +1 -1
- package/package.json +104 -104
- package/readme.md +343 -343
- package/src/assets/BlocklyLogo.tsx +27 -27
- package/src/assets/Distance.tsx +18 -18
- package/src/assets/JogLong.tsx +13 -13
- package/src/assets/JogMedium.tsx +13 -13
- package/src/assets/JogShort.tsx +13 -13
- package/src/assets/PythonLogo.tsx +83 -83
- package/src/assets/Rotation3D.tsx +13 -13
- package/src/assets/RotationCcw.tsx +33 -33
- package/src/assets/RotationCcwA.tsx +45 -45
- package/src/assets/RotationCcwB.tsx +45 -45
- package/src/assets/RotationCcwC.tsx +45 -45
- package/src/assets/RotationCw.tsx +31 -31
- package/src/assets/RotationCwA.tsx +42 -42
- package/src/assets/RotationCwB.tsx +42 -42
- package/src/assets/RotationCwC.tsx +42 -42
- package/src/assets/Run.tsx +13 -13
- package/src/assets/Speed.tsx +18 -18
- package/src/assets/SpeedFast.tsx +13 -13
- package/src/assets/SpeedMedium.tsx +13 -13
- package/src/assets/SpeedNone.tsx +13 -13
- package/src/assets/SpeedSlow.tsx +13 -13
- package/src/assets/Walk.tsx +13 -13
- package/src/assets/index.ts +22 -22
- package/src/assets/svg/blockly_logo.svg +82 -82
- package/src/assets/svg/distance.svg +40 -40
- package/src/assets/svg/python_logo.svg +246 -246
- package/src/assets/svg/rotation_ccw.svg +50 -50
- package/src/assets/svg/rotation_ccw_a.svg +57 -57
- package/src/assets/svg/rotation_ccw_b.svg +57 -57
- package/src/assets/svg/rotation_ccw_c.svg +57 -57
- package/src/assets/svg/rotation_cw.svg +49 -49
- package/src/assets/svg/rotation_cw_a.svg +30 -30
- package/src/assets/svg/rotation_cw_b.svg +30 -30
- package/src/assets/svg/rotation_cw_c.svg +30 -30
- package/src/assets/svg/speed.svg +39 -39
- package/src/components/AutoCoreDevPanel.tsx +414 -414
- package/src/components/BlocklyEditor.css +93 -93
- package/src/components/BlocklyEditor.tsx +609 -609
- package/src/components/CodeEditor.tsx +155 -155
- package/src/components/FileList.tsx +390 -390
- package/src/components/FileSelect.tsx +128 -128
- package/src/components/FitText.tsx +35 -35
- package/src/components/Indicator.tsx +188 -188
- package/src/components/IndicatorButton.tsx +214 -214
- package/src/components/IndicatorRect.tsx +172 -172
- package/src/components/JogPanel.css +41 -41
- package/src/components/JogPanel.tsx +461 -461
- package/src/components/Lamp.tsx +243 -243
- package/src/components/Osk.tsx +192 -192
- package/src/components/OskDialog.tsx +164 -164
- package/src/components/ProgressBarWithValue.css +27 -27
- package/src/components/ProgressBarWithValue.tsx +48 -48
- package/src/components/TextInput.tsx +195 -195
- package/src/components/ToggleGroup.tsx +322 -322
- package/src/components/ValueDisplay.tsx +236 -236
- package/src/components/ValueIndicator.css +31 -31
- package/src/components/ValueIndicator.tsx +135 -135
- package/src/components/ValueInput.tsx +368 -368
- package/src/components/osk.css +123 -123
- package/src/core/ActionMode.ts +19 -19
- package/src/core/AutoCoreTagContext.tsx +625 -614
- package/src/core/AutoCoreTagTypes.ts +334 -334
- package/src/core/CoreStreamTypes.ts +512 -512
- package/src/core/EventEmitterContext.tsx +434 -434
- package/src/core/IndicatorButtonState.ts +34 -34
- package/src/core/IndicatorColor.ts +35 -35
- package/src/core/MaskPatterns.ts +87 -87
- package/src/core/NumerableTypes.ts +80 -80
- package/src/core/PositionContext.ts +59 -59
- package/src/core/UniqueId.ts +41 -41
- package/src/core/ValueSimulator.ts +166 -166
- package/src/core/hoc.tsx +65 -65
- package/src/hooks/adsHooks.tsx +287 -287
- package/src/hooks/commandHooks.tsx +300 -300
- package/src/hooks/index.ts +12 -12
- package/src/hooks/useAutoCoreTag.ts +103 -103
- package/src/hooks/useScaledValue.tsx +99 -99
- package/src/hub/CommandMessage.ts +89 -89
- package/src/hub/DebugPanel.ts +307 -307
- package/src/hub/HubBase.ts +249 -236
- package/src/hub/HubSimulate.ts +124 -124
- package/src/hub/HubTauri.ts +140 -140
- package/src/hub/HubWebSocket.ts +250 -250
- package/src/hub/debug.ts +211 -211
- package/src/hub/index.ts +81 -81
- package/src/themes/adc-dark/_extensions.scss +166 -166
- package/src/themes/adc-dark/_variables.scss +913 -913
- package/src/themes/adc-dark/blue/_fonts.scss +23 -23
- package/src/themes/adc-dark/blue/adc_theme.scss +31 -31
- package/src/themes/adc-dark/blue/theme.scss +14 -14
- package/src/themes/theme-base/_colors.scss +17 -17
- package/src/themes/theme-base/_common.scss +74 -74
- package/src/themes/theme-base/_components.scss +111 -111
- package/src/themes/theme-base/_mixins.scss +243 -243
- package/src/themes/theme-base/components/button/_button.scss +644 -644
- package/src/themes/theme-base/components/button/_speeddial.scss +91 -91
- package/src/themes/theme-base/components/button/_splitbutton.scss +358 -358
- package/src/themes/theme-base/components/data/_carousel.scss +39 -39
- package/src/themes/theme-base/components/data/_datascroller.scss +47 -47
- package/src/themes/theme-base/components/data/_datatable.scss +388 -388
- package/src/themes/theme-base/components/data/_dataview.scss +47 -47
- package/src/themes/theme-base/components/data/_filter.scss +137 -137
- package/src/themes/theme-base/components/data/_orderlist.scss +86 -86
- package/src/themes/theme-base/components/data/_organizationchart.scss +50 -50
- package/src/themes/theme-base/components/data/_paginator.scss +91 -91
- package/src/themes/theme-base/components/data/_picklist.scss +73 -73
- package/src/themes/theme-base/components/data/_timeline.scss +38 -38
- package/src/themes/theme-base/components/data/_tree.scss +184 -184
- package/src/themes/theme-base/components/data/_treetable.scss +431 -431
- package/src/themes/theme-base/components/file/_fileupload.scss +41 -41
- package/src/themes/theme-base/components/input/_autocomplete.scss +94 -94
- package/src/themes/theme-base/components/input/_calendar.scss +251 -251
- package/src/themes/theme-base/components/input/_cascadeselect.scss +107 -107
- package/src/themes/theme-base/components/input/_checkbox.scss +181 -181
- package/src/themes/theme-base/components/input/_chips.scss +102 -102
- package/src/themes/theme-base/components/input/_colorpicker.scss +17 -17
- package/src/themes/theme-base/components/input/_dropdown.scss +252 -252
- package/src/themes/theme-base/components/input/_editor.scss +122 -122
- package/src/themes/theme-base/components/input/_iconfield.scss +9 -9
- package/src/themes/theme-base/components/input/_inputgroup.scss +74 -74
- package/src/themes/theme-base/components/input/_inputicon.scss +14 -14
- package/src/themes/theme-base/components/input/_inputnumber.scss +4 -4
- package/src/themes/theme-base/components/input/_inputotp.scss +10 -10
- package/src/themes/theme-base/components/input/_inputswitch.scss +99 -99
- package/src/themes/theme-base/components/input/_inputtext.scss +101 -101
- package/src/themes/theme-base/components/input/_listbox.scss +138 -138
- package/src/themes/theme-base/components/input/_mention.scss +30 -30
- package/src/themes/theme-base/components/input/_multiselect.scss +278 -278
- package/src/themes/theme-base/components/input/_password.scss +32 -32
- package/src/themes/theme-base/components/input/_radiobutton.scss +169 -169
- package/src/themes/theme-base/components/input/_rating.scss +80 -80
- package/src/themes/theme-base/components/input/_selectbutton.scss +49 -49
- package/src/themes/theme-base/components/input/_slider.scss +49 -49
- package/src/themes/theme-base/components/input/_togglebutton.scss +99 -99
- package/src/themes/theme-base/components/input/_treeselect.scss +151 -151
- package/src/themes/theme-base/components/input/_tristatecheckbox.scss +46 -46
- package/src/themes/theme-base/components/menu/_breadcrumb.scss +42 -42
- package/src/themes/theme-base/components/menu/_contextmenu.scss +39 -39
- package/src/themes/theme-base/components/menu/_dock.scss +109 -109
- package/src/themes/theme-base/components/menu/_megamenu.scss +141 -141
- package/src/themes/theme-base/components/menu/_menu.scss +33 -33
- package/src/themes/theme-base/components/menu/_menubar.scss +216 -216
- package/src/themes/theme-base/components/menu/_panelmenu.scss +153 -153
- package/src/themes/theme-base/components/menu/_slidemenu.scss +60 -60
- package/src/themes/theme-base/components/menu/_steps.scss +57 -57
- package/src/themes/theme-base/components/menu/_tabmenu.scss +50 -50
- package/src/themes/theme-base/components/menu/_tieredmenu.scss +43 -43
- package/src/themes/theme-base/components/messages/_inlinemessage.scss +69 -69
- package/src/themes/theme-base/components/messages/_message.scss +107 -107
- package/src/themes/theme-base/components/messages/_toast.scss +100 -100
- package/src/themes/theme-base/components/misc/_avatar.scss +33 -33
- package/src/themes/theme-base/components/misc/_badge.scss +76 -76
- package/src/themes/theme-base/components/misc/_chip.scss +38 -38
- package/src/themes/theme-base/components/misc/_inplace.scss +17 -17
- package/src/themes/theme-base/components/misc/_metergroup.scss +80 -80
- package/src/themes/theme-base/components/misc/_progressbar.scss +17 -17
- package/src/themes/theme-base/components/misc/_scrolltop.scss +24 -24
- package/src/themes/theme-base/components/misc/_skeleton.scss +7 -7
- package/src/themes/theme-base/components/misc/_tag.scss +39 -39
- package/src/themes/theme-base/components/misc/_terminal.scss +12 -12
- package/src/themes/theme-base/components/multimedia/_galleria.scss +153 -153
- package/src/themes/theme-base/components/multimedia/_image.scss +53 -53
- package/src/themes/theme-base/components/overlay/_confirmpopup.scss +72 -72
- package/src/themes/theme-base/components/overlay/_dialog.scss +78 -78
- package/src/themes/theme-base/components/overlay/_overlaypanel.scss +64 -64
- package/src/themes/theme-base/components/overlay/_sidebar.scss +23 -23
- package/src/themes/theme-base/components/overlay/_tooltip.scss +33 -33
- package/src/themes/theme-base/components/panel/_accordion.scss +118 -118
- package/src/themes/theme-base/components/panel/_card.scss +30 -30
- package/src/themes/theme-base/components/panel/_divider.scss +30 -30
- package/src/themes/theme-base/components/panel/_fieldset.scss +47 -47
- package/src/themes/theme-base/components/panel/_panel.scss +47 -47
- package/src/themes/theme-base/components/panel/_scrollpanel.scss +10 -10
- package/src/themes/theme-base/components/panel/_splitter.scss +23 -23
- package/src/themes/theme-base/components/panel/_stepper.scss +136 -136
- package/src/themes/theme-base/components/panel/_tabview.scss +147 -147
- package/src/themes/theme-base/components/panel/_toolbar.scss +11 -11
- package/terser.config.cjs +25 -25
- package/todo.md +18 -18
- package/tools/build-themes.cjs +65 -65
- package/tools/copy-distribution-files.cjs +77 -77
- package/tools/minify.cjs +55 -55
- package/tsconfig.json +48 -48
- package/typedoc.json +12 -12
- package/.claude/settings.local.json +0 -7
|
@@ -1,441 +1,441 @@
|
|
|
1
|
-
# AutoCoreTagContext Manual
|
|
2
|
-
|
|
3
|
-
This manual explains how to configure, provide, and consume the **AutoCoreTagContext** system.
|
|
4
|
-
The goal is to create a **typed, global context** of tags (values from `autocore-server`), so your React components can read and update them easily.
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Overview
|
|
9
|
-
|
|
10
|
-
The AutoCoreTagContext is built around three parts:
|
|
11
|
-
|
|
12
|
-
1. **Context** – created with `makeAutoCoreTagContext` to hold live tag values.
|
|
13
|
-
2. **Hooks** – created with `makeAutoCoreTagHooks` to read, write, tap, and derive values.
|
|
14
|
-
3. **Dev Panel** – created with `makeAutoCoreDevPanel` to inspect and manually interact with tags during development.
|
|
15
|
-
|
|
16
|
-
Tags are defined in a `spec` object: an array of `TagConfig` entries that tell the system what to subscribe to.
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Summary
|
|
21
|
-
|
|
22
|
-
- Define tags once in a spec.
|
|
23
|
-
- Create context, hooks, and optionally a Dev Panel.
|
|
24
|
-
- Provide the context at the root of your app.
|
|
25
|
-
- Consume via:
|
|
26
|
-
- useAutoCoreTag → single value + actions.
|
|
27
|
-
- useAutoCoreTags → bulk values.
|
|
28
|
-
- useAutoCoreSelect → computed state.
|
|
29
|
-
|
|
30
|
-
This makes your React UI a strongly-typed, declarative layer over the live tag system from autocore-server.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## Defining Your Tags and AutoCoreHooks
|
|
34
|
-
|
|
35
|
-
In this initial step, we make two files:
|
|
36
|
-
1. AutoCoreTags.ts
|
|
37
|
-
2. AutoCore.ts
|
|
38
|
-
|
|
39
|
-
Note that ADC is working to create a VS Code extension for help auto-generating these files.
|
|
40
|
-
|
|
41
|
-
### AutoCoreTags.ts
|
|
42
|
-
|
|
43
|
-
To maintain type safety, tags are defined and exported from a TypeScript file. Tags are defined as an array of TagConfig objects.
|
|
44
|
-
|
|
45
|
-
Each tag has:
|
|
46
|
-
- `tagName` – your local name for the tag.
|
|
47
|
-
- `domain` – the domain for the symbol (e.g., `"ADS"`, `"MEMORYSTORE"`).
|
|
48
|
-
- `symbolName` – the backend symbol name.
|
|
49
|
-
- `valueType` – `"boolean"`, `"number"`, `"string"`, or `"json"`.
|
|
50
|
-
- `initialValue` (optional) – value before first update.
|
|
51
|
-
- `options` (optional) – advanced subscription options.
|
|
52
|
-
|
|
53
|
-
By convention, the TypeScript file is located in the same directory as App.tsx and named AutoCoreTags.ts. The exported object is named acTagSpec.
|
|
54
|
-
|
|
55
|
-
Example:
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
import type { TagConfig } from "@adcops/autocore-react/core/AutoCoreTagTypes";
|
|
59
|
-
|
|
60
|
-
export const acTagSpec = [
|
|
61
|
-
{ "tagName": "isControlPowerOk", "domain": "ADS", "symbolName": "GIO.xbControlPowerOk", "valueType": "boolean" },
|
|
62
|
-
{ "tagName": "positionScalar", "domain": "MEMORYSTORE", "symbolName": "position_scalar", "valueType": "number", "initialValue": 1.0 },
|
|
63
|
-
{ "tagName": "isAutoCycleRunning", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bAutoCycleRunning", "valueType": "boolean" },
|
|
64
|
-
{ "tagName": "isAutoCycleRunning", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bAutoCycleRunning", "valueType": "boolean" },
|
|
65
|
-
{ "tagName": "shuttlePosition", "domain": "ADS", "symbolName": "MAIN.ctx.gio.axisShuttle.fPosition", "valueType": "number" },
|
|
66
|
-
{ "tagName": "pressLoad", "domain": "ADS", "symbolName": "MAIN.ctx.gm.fPressLoad", "valueType": "number" },
|
|
67
|
-
{ "tagName": "pressPosition", "domain": "ADS", "symbolName": "MAIN.ctx.gm.fPressPosition", "valueType": "number" },
|
|
68
|
-
{ "tagName": "pressPeakLoad", "domain": "ADS", "symbolName": "MAIN.ctx.gm.fPressPeakLoad", "valueType": "number" },
|
|
69
|
-
{ "tagName": "isReadyForCycle", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bReadyForCycle", "valueType": "boolean" },
|
|
70
|
-
{ "tagName": "reqStartAuto", "domain": "ADS", "symbolName": "MAIN.ctx.gsig.bSigStartAuto", "valueType": "boolean" },
|
|
71
|
-
{ "tagName": "useSecondaryLoadChannel", "domain": "ADS", "symbolName": "MAIN.ctx.gnv.bEnableSecondaryLoadInput", "valueType": "boolean" },
|
|
72
|
-
{ "tagName": "isLoadCellOverrange", "domain": "ADS", "symbolName": "MAIN.ctx.gio.xstLoadInputStatus.IsOverrange", "valueType": "boolean" },
|
|
73
|
-
{ "tagName": "isLoadCellUnderrange", "domain": "ADS", "symbolName": "MAIN.ctx.gio.xstLoadInputStatus.IsUnderrange", "valueType": "boolean" },
|
|
74
|
-
{ "tagName": "isMotorsOn", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bAllMotorsOn", "valueType": "boolean" },
|
|
75
|
-
{ "tagName": "isAxisErrorPresent", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bAxisErrorPresent", "valueType": "boolean" },
|
|
76
|
-
{ "tagName": "isAllAxesHomed", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bAllAxesHomed", "valueType": "boolean" },
|
|
77
|
-
{ "tagName": "isReadyForOperation", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bReadyForOperation", "valueType": "boolean" }
|
|
78
|
-
] as const satisfies readonly TagConfig[];
|
|
79
|
-
|
|
80
|
-
export default acTagSpec;
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### AutoCore.ts
|
|
84
|
-
|
|
85
|
-
Once the tags are created, we create a simple TypeScript file to make and provide the hooks throughout the application. By producing typed hooks, Intellisense is able to provide support for tag names and value types.
|
|
86
|
-
|
|
87
|
-
Create AutoCore.ts in the same directory as App.tsx and AutoCoreTags.ts. The template below should work for any project.
|
|
88
|
-
|
|
89
|
-
```ts
|
|
90
|
-
/**
|
|
91
|
-
* @module src/AutoCore
|
|
92
|
-
* @preferred
|
|
93
|
-
*
|
|
94
|
-
* Strongly-typed hooks for AutoCore tags.
|
|
95
|
-
* Could be automatically generated by autocore-react plugin.
|
|
96
|
-
*
|
|
97
|
-
* This module binds your app’s tag spec (from `tags.json` or `tags-spec.ts`) to the
|
|
98
|
-
* global `AutoCoreTagContext`, producing **typed hooks** that provide perfect
|
|
99
|
-
* IntelliSense for tag names and value types.
|
|
100
|
-
*
|
|
101
|
-
* @remarks
|
|
102
|
-
* - Pair this with the runtime provider in your app root:
|
|
103
|
-
* `AutoCoreTagProvider` wraps `<App/>` and receives the same `spec`.
|
|
104
|
-
* - These hooks expose:
|
|
105
|
-
* - `useAutoCoreTag(tagName)` → `{ value, write, tap, isLoading, ... }`
|
|
106
|
-
* - `useAutoCoreTags(tagNames[])` → `{ values, set, isLoading }`
|
|
107
|
-
* - `useAutoCoreSelect(selector)` → `{ selected, isLoading }`
|
|
108
|
-
* - Keep `tags.json` (or `tags.ts`) as the single source of truth. If using JSON,
|
|
109
|
-
* validate and narrow it before export so `valueType` is the literal union
|
|
110
|
-
* `"boolean" | "number" | "string" | "json"`.
|
|
111
|
-
*
|
|
112
|
-
* @example
|
|
113
|
-
* ```ts
|
|
114
|
-
* // src/AutoCore.ts
|
|
115
|
-
* import { AutoCoreTagContext } from "@adcops/autocore-react/core/AutoCoreTagContext";
|
|
116
|
-
* import { makeAutoCoreTagHooks } from "@adcops/autocore-react/hooks/useAutoCoreTag";
|
|
117
|
-
* import { spec } from "./tags-spec"; // wraps ./tags.json and exports a narrowed, validated spec
|
|
118
|
-
*
|
|
119
|
-
* export const AutoCoreHooks = makeAutoCoreTagHooks(AutoCoreTagContext, spec);
|
|
120
|
-
* ```
|
|
121
|
-
*
|
|
122
|
-
* @example
|
|
123
|
-
* ```tsx
|
|
124
|
-
* // App.tsx (runtime provider)
|
|
125
|
-
* import rawSpec from "./tags.json" assert { type: "json" };
|
|
126
|
-
* import { AutoCoreTagProvider } from "@adcops/autocore-react/core/AutoCoreTagContext";
|
|
127
|
-
*
|
|
128
|
-
* <EventEmitterProvider>
|
|
129
|
-
* <AutoCoreTagProvider tags={rawSpec} eagerRead>
|
|
130
|
-
* <App/>
|
|
131
|
-
* </AutoCoreTagProvider>
|
|
132
|
-
* </EventEmitterProvider>
|
|
133
|
-
* ```
|
|
134
|
-
*
|
|
135
|
-
* @example
|
|
136
|
-
* ```tsx
|
|
137
|
-
* // Any component
|
|
138
|
-
* import { AutoCoreHooks } from "./AutoCore";
|
|
139
|
-
*
|
|
140
|
-
* const { value: solForward, tap } = AutoCoreHooks.useAutoCoreTag("gio.solForward");
|
|
141
|
-
* const { value: preset, write } = AutoCoreHooks.useAutoCoreTag("gm.ton.tPreset");
|
|
142
|
-
*
|
|
143
|
-
* const { values } = AutoCoreHooks.useAutoCoreTags(["isDoorClosed", "isControlPowerOk"]);
|
|
144
|
-
*
|
|
145
|
-
* const { selected: ready } = AutoCoreHooks.useAutoCoreSelect(v =>
|
|
146
|
-
* !!v["isDoorClosed"] && !!v["isControlPowerOk"]
|
|
147
|
-
* );
|
|
148
|
-
* ```
|
|
149
|
-
*
|
|
150
|
-
* @see {@link @adcops/autocore-react/core/AutoCoreTagContext.AutoCoreTagProvider | AutoCoreTagProvider}
|
|
151
|
-
* @see {@link @adcops/autocore-react/hooks/useAutoCoreTag.makeAutoCoreTagHooks | makeAutoCoreTagHooks}
|
|
152
|
-
*/
|
|
153
|
-
|
|
154
|
-
import {acTagSpec} from "./AutoCoreTags";
|
|
155
|
-
import { AutoCoreTagContext } from "@adcops/autocore-react/core/AutoCoreTagContext";
|
|
156
|
-
import { makeAutoCoreTagHooks } from "@adcops/autocore-react/hooks/useAutoCoreTag";
|
|
157
|
-
|
|
158
|
-
export const AutoCoreHooks = makeAutoCoreTagHooks(AutoCoreTagContext, acTagSpec);
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
# Providing Context to the App
|
|
163
|
-
|
|
164
|
-
With our autocore tags and hooks defined, we now wrap our app in the provided context. Make sure to wrap the application in both the EventEmitterProvider and AutoCoreTagProvider. The example shown also uses primereact.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
```ts
|
|
168
|
-
import { PrimeReactProvider} from 'primereact/api';
|
|
169
|
-
import { EventEmitterProvider } from "@adcops/autocore-react/core/EventEmitterContext.js";
|
|
170
|
-
import {AutoCoreTagProvider} from "@adcops/autocore-react/core/AutoCoreTagContext";
|
|
171
|
-
import {acTagSpec} from "./AutoCoreTags";
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// 3. Developer panel
|
|
175
|
-
export const AutoCorePanel = makeAutoCoreDevPanel(AutoCore.Context, spec);
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
# Providing Context to Your App
|
|
180
|
-
|
|
181
|
-
Wrap your application with the provider:
|
|
182
|
-
|
|
183
|
-
```tsx
|
|
184
|
-
function App() {
|
|
185
|
-
return(
|
|
186
|
-
<EventEmitterProvider>
|
|
187
|
-
<PrimeReactProvider>
|
|
188
|
-
<AutoCoreTagProvider tags={acTagSpec} eagerRead>
|
|
189
|
-
<div className="app-wrapper">
|
|
190
|
-
|
|
191
|
-
<main className="main-wrapper">
|
|
192
|
-
|
|
193
|
-
<section className="content-wrapper">
|
|
194
|
-
<ContentView />
|
|
195
|
-
</section>
|
|
196
|
-
|
|
197
|
-
</main>
|
|
198
|
-
|
|
199
|
-
<footer className="footer-wrapper">
|
|
200
|
-
<FooterView />
|
|
201
|
-
</footer>
|
|
202
|
-
|
|
203
|
-
</div>
|
|
204
|
-
</AutoCoreTagProvider>
|
|
205
|
-
</PrimeReactProvider>
|
|
206
|
-
</EventEmitterProvider>
|
|
207
|
-
)
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
- `eagerRead` (default: true) triggers initial reads so values are populated immediately.
|
|
212
|
-
|
|
213
|
-
# Using the Hooks
|
|
214
|
-
|
|
215
|
-
1. useAutoCoreTag(tagName)
|
|
216
|
-
|
|
217
|
-
Access a single tag. Provides:
|
|
218
|
-
- `value` – current value.
|
|
219
|
-
- `setValue` / write – optimistic setter (updates context + backend).
|
|
220
|
-
- `writeRaw` – direct backend write, no optimistic update.
|
|
221
|
-
- `tap` – momentary true → false action (for booleans).
|
|
222
|
-
- `isLoading` – whether the initial read is pending.
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
2. useAutoCoreTags([tagNames])
|
|
226
|
-
|
|
227
|
-
Access multiple tags at once. Provides:
|
|
228
|
-
|
|
229
|
-
- `values` – object with each tag’s current value.
|
|
230
|
-
- `set(tagName, value)` – update any tag in the set.
|
|
231
|
-
- `isLoading` – whether the initial read is pending.
|
|
232
|
-
|
|
233
|
-
3. useAutoCoreSelect(selector)
|
|
234
|
-
|
|
235
|
-
Derive a computed value from the full tag state.
|
|
236
|
-
|
|
237
|
-
```tsx
|
|
238
|
-
const { selected: ready } = AutoCoreHooks.useAutoCoreSelect(v =>
|
|
239
|
-
!!v["gio.solForward"] && (v["gm.ton.tPreset"] ?? 0) > 0
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
return <Button disabled={!ready}>Start</Button>;
|
|
243
|
-
```
|
|
244
|
-
- `ready` is recomputed any time gio.solForward or gm.ton.tPreset changes.
|
|
245
|
-
- Great for business logic conditions.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
# Automatic Scaling
|
|
249
|
-
|
|
250
|
-
AutoCoreTagContext supports automatic unit conversion through named scales with server synchronization. Scale factors are automatically kept in sync with autocore-server values, enabling user-configurable units that persist across sessions.
|
|
251
|
-
|
|
252
|
-
### How Scaling Works
|
|
253
|
-
|
|
254
|
-
- Incoming values: Server values are multiplied by the scale factor before being stored
|
|
255
|
-
- Outgoing values: Display values are divided by the scale factor before being sent to server
|
|
256
|
-
- Components see scaled values: All hook operations work with display-friendly values
|
|
257
|
-
- Automatic sync: Scale factors with serverTag properties are automatically synchronized with their server values.
|
|
258
|
-
- Real-time updates: Scale changes take effect immediately across the application.
|
|
259
|
-
- Consistent display: All components automatically show the same units.
|
|
260
|
-
- Dual subscriptions: The system maintains separate subscriptions for data tags and scale tags.
|
|
261
|
-
- No manual scaling needed: Eliminates the need for `useScaledValue` hooks
|
|
262
|
-
- Prevents value flicker. Values are scaled before being displayed.
|
|
263
|
-
- Type-safe. Scale names are validated at compile time.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
Server-Driven Scales are preferred.
|
|
267
|
-
- Scale factors can come from autocore-server tags for user-configurable units.
|
|
268
|
-
- The autocore-server GNV domain will store changes in non-volatile memory.
|
|
269
|
-
- Automatic synchronization: Multiple clients stay in sync
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
## Defining Server-Driven Scales
|
|
273
|
-
|
|
274
|
-
Create AutoCoreScales.ts with server tag references:
|
|
275
|
-
|
|
276
|
-
```tsx
|
|
277
|
-
import { ScaleConfig } from "@adcops/autocore-react/core/AutoCoreTagTypes";
|
|
278
|
-
|
|
279
|
-
export const acScales = {
|
|
280
|
-
position: {
|
|
281
|
-
name: "position",
|
|
282
|
-
scale: 1.0,
|
|
283
|
-
label: "mm",
|
|
284
|
-
serverTag: { domain: "MEMORYSTORE", symbolName: "position_scalar" }
|
|
285
|
-
},
|
|
286
|
-
load: {
|
|
287
|
-
name: "load",
|
|
288
|
-
scale: 1.0,
|
|
289
|
-
label: "N",
|
|
290
|
-
serverTag: { domain: "MEMORYSTORE", symbolName: "load_scalar" }
|
|
291
|
-
},
|
|
292
|
-
temperature: {
|
|
293
|
-
name: "temperature",
|
|
294
|
-
scale: 1.0,
|
|
295
|
-
label: "°C"
|
|
296
|
-
// No serverTag - static scale only
|
|
297
|
-
}
|
|
298
|
-
} as const satisfies Record<string, ScaleConfig>;
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
Then reference scales in the tag definitions:
|
|
302
|
-
```tsx
|
|
303
|
-
// In AutoCoreTags.ts - add scale property to numeric tags
|
|
304
|
-
{ "tagName": "pressPosition", "domain": "ADS", "symbolName": "MAIN.ctx.gm.fPressPosition", "valueType": "number", "scale": "position" },
|
|
305
|
-
{ "tagName": "pressLoad", "domain": "ADS", "symbolName": "MAIN.ctx.gm.fPressLoad", "valueType": "number", "scale": "load" }
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
## Provider Configuration
|
|
310
|
-
|
|
311
|
-
Pass scales to the provider:
|
|
312
|
-
|
|
313
|
-
```tsx
|
|
314
|
-
import { acScales } from "./AutoCoreScales";
|
|
315
|
-
|
|
316
|
-
<AutoCoreTagProvider tags={acTagSpec} scales={acScales} eagerRead>
|
|
317
|
-
<App/>
|
|
318
|
-
</AutoCoreTagProvider>
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
## Updating Scale Factors
|
|
323
|
-
|
|
324
|
-
Use the updateScale function to change units at runtime.
|
|
325
|
-
|
|
326
|
-
`updateScale(scaleName, newScale, newLabel)`
|
|
327
|
-
|
|
328
|
-
Always provide both scale factor and label when calling `updateScale`.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
Example:
|
|
332
|
-
```tsx
|
|
333
|
-
import { AutoCoreHooks } from "./AutoCore";
|
|
334
|
-
|
|
335
|
-
const UnitControls: React.FC = () => {
|
|
336
|
-
const { updateScale, getScale } = AutoCoreHooks.useScales();
|
|
337
|
-
|
|
338
|
-
const switchToMetric = async () => {
|
|
339
|
-
await updateScale("position", 1.0, "mm");
|
|
340
|
-
await updateScale("load", 1.0, "N");
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
const switchToImperial = async () => {
|
|
344
|
-
await updateScale("position", 1/25.4, "in");
|
|
345
|
-
await updateScale("load", 0.224809, "lbs");
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
return (
|
|
349
|
-
<div>
|
|
350
|
-
<button onClick={switchToMetric}>Metric Units</button>
|
|
351
|
-
<button onClick={switchToImperial}>Imperial Units</button>
|
|
352
|
-
</div>
|
|
353
|
-
);
|
|
354
|
-
};
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### Backward Compatibility
|
|
358
|
-
The system handles legacy installations where only numeric scale factors were stored. When legacy numeric data is received from the server, the label is set to "---" to indicate that proper units should be configured.
|
|
359
|
-
|
|
360
|
-
```tsx
|
|
361
|
-
const positionScale = getScale("position");
|
|
362
|
-
if (positionScale?.label === "---") {
|
|
363
|
-
// Legacy data detected - prompt user to set proper units
|
|
364
|
-
return <div>Please configure display units in settings</div>;
|
|
365
|
-
}
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
## Server Data Format
|
|
369
|
-
Scale data is stored on the server as JSON objects containing both scale factor and display label.
|
|
370
|
-
|
|
371
|
-
```json
|
|
372
|
-
{
|
|
373
|
-
"scale": 1.0,
|
|
374
|
-
"label": "mm"
|
|
375
|
-
}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
# Passing Tag Hooks as Component Props
|
|
379
|
-
When building reusable components that work with AutoCore tags, you can pass entire tag hook results as props. This provides the component with both the current value and the ability to write/tap the tag.
|
|
380
|
-
|
|
381
|
-
Use ReturnType<typeof AutoCoreHooks.useAutoCoreTag> to properly type tag hook props.
|
|
382
|
-
|
|
383
|
-
```tsx
|
|
384
|
-
interface LoadChannelSettingsProps {
|
|
385
|
-
capacitySetTag: ReturnType<typeof AutoCoreHooks.useAutoCoreTag>;
|
|
386
|
-
enabledTag: ReturnType<typeof AutoCoreHooks.useAutoCoreTag>;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const LoadChannelSettings: React.FC<LoadChannelSettingsProps> = ({
|
|
390
|
-
capacitySetTag,
|
|
391
|
-
enabledTag
|
|
392
|
-
}) => {
|
|
393
|
-
const { value: capacity, write: setCapacity } = capacitySetTag;
|
|
394
|
-
const { value: enabled, tap: toggleEnabled } = enabledTag;
|
|
395
|
-
|
|
396
|
-
return (
|
|
397
|
-
<div>
|
|
398
|
-
<input
|
|
399
|
-
type="number"
|
|
400
|
-
value={capacity ?? 0}
|
|
401
|
-
onChange={e => setCapacity(Number(e.target.value))}
|
|
402
|
-
/>
|
|
403
|
-
<button onClick={toggleEnabled}>
|
|
404
|
-
{enabled ? "Disable" : "Enable"}
|
|
405
|
-
</button>
|
|
406
|
-
</div>
|
|
407
|
-
);
|
|
408
|
-
};
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
### Usage
|
|
412
|
-
|
|
413
|
-
Pass the complete hook result to the component.
|
|
414
|
-
|
|
415
|
-
```tsx
|
|
416
|
-
<LoadChannelSettings
|
|
417
|
-
capacitySetTag={AutoCoreHooks.useAutoCoreTag("loadCellCapacitySet")}
|
|
418
|
-
enabledTag={AutoCoreHooks.useAutoCoreTag("loadChannelEnabled")}
|
|
419
|
-
/>
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
# DevPanel
|
|
423
|
-
For debugging, include the Dev Panel in your app:
|
|
424
|
-
|
|
425
|
-
```tsx
|
|
426
|
-
<AutoCorePanel className="fixed bottom-4 right-4 bg-black/60 text-white" />
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
It shows:
|
|
430
|
-
- Each tag’s name, type, current value.
|
|
431
|
-
- Editable input fields for live writes/taps.
|
|
432
|
-
- Loading/live status indicator.
|
|
433
|
-
|
|
434
|
-
# Best Practices
|
|
435
|
-
|
|
436
|
-
- Always define your tags in one spec to keep types consistent across your app.
|
|
437
|
-
- Prefer write (optimistic) for user-driven updates; use writeRaw only if you need backend confirmation before updating UI.
|
|
438
|
-
- Use tap only for boolean momentary actions (reset, trigger, etc.).
|
|
439
|
-
- Use useAutoCoreSelect to express high-level conditions (isSafeToStart, readyToCycle) instead of scattering logic through components.
|
|
440
|
-
- Keep the Dev Panel available in development to quickly test and validate your tag interactions.
|
|
441
|
-
|
|
1
|
+
# AutoCoreTagContext Manual
|
|
2
|
+
|
|
3
|
+
This manual explains how to configure, provide, and consume the **AutoCoreTagContext** system.
|
|
4
|
+
The goal is to create a **typed, global context** of tags (values from `autocore-server`), so your React components can read and update them easily.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
The AutoCoreTagContext is built around three parts:
|
|
11
|
+
|
|
12
|
+
1. **Context** – created with `makeAutoCoreTagContext` to hold live tag values.
|
|
13
|
+
2. **Hooks** – created with `makeAutoCoreTagHooks` to read, write, tap, and derive values.
|
|
14
|
+
3. **Dev Panel** – created with `makeAutoCoreDevPanel` to inspect and manually interact with tags during development.
|
|
15
|
+
|
|
16
|
+
Tags are defined in a `spec` object: an array of `TagConfig` entries that tell the system what to subscribe to.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Summary
|
|
21
|
+
|
|
22
|
+
- Define tags once in a spec.
|
|
23
|
+
- Create context, hooks, and optionally a Dev Panel.
|
|
24
|
+
- Provide the context at the root of your app.
|
|
25
|
+
- Consume via:
|
|
26
|
+
- useAutoCoreTag → single value + actions.
|
|
27
|
+
- useAutoCoreTags → bulk values.
|
|
28
|
+
- useAutoCoreSelect → computed state.
|
|
29
|
+
|
|
30
|
+
This makes your React UI a strongly-typed, declarative layer over the live tag system from autocore-server.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## Defining Your Tags and AutoCoreHooks
|
|
34
|
+
|
|
35
|
+
In this initial step, we make two files:
|
|
36
|
+
1. AutoCoreTags.ts
|
|
37
|
+
2. AutoCore.ts
|
|
38
|
+
|
|
39
|
+
Note that ADC is working to create a VS Code extension for help auto-generating these files.
|
|
40
|
+
|
|
41
|
+
### AutoCoreTags.ts
|
|
42
|
+
|
|
43
|
+
To maintain type safety, tags are defined and exported from a TypeScript file. Tags are defined as an array of TagConfig objects.
|
|
44
|
+
|
|
45
|
+
Each tag has:
|
|
46
|
+
- `tagName` – your local name for the tag.
|
|
47
|
+
- `domain` – the domain for the symbol (e.g., `"ADS"`, `"MEMORYSTORE"`).
|
|
48
|
+
- `symbolName` – the backend symbol name.
|
|
49
|
+
- `valueType` – `"boolean"`, `"number"`, `"string"`, or `"json"`.
|
|
50
|
+
- `initialValue` (optional) – value before first update.
|
|
51
|
+
- `options` (optional) – advanced subscription options.
|
|
52
|
+
|
|
53
|
+
By convention, the TypeScript file is located in the same directory as App.tsx and named AutoCoreTags.ts. The exported object is named acTagSpec.
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import type { TagConfig } from "@adcops/autocore-react/core/AutoCoreTagTypes";
|
|
59
|
+
|
|
60
|
+
export const acTagSpec = [
|
|
61
|
+
{ "tagName": "isControlPowerOk", "domain": "ADS", "symbolName": "GIO.xbControlPowerOk", "valueType": "boolean" },
|
|
62
|
+
{ "tagName": "positionScalar", "domain": "MEMORYSTORE", "symbolName": "position_scalar", "valueType": "number", "initialValue": 1.0 },
|
|
63
|
+
{ "tagName": "isAutoCycleRunning", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bAutoCycleRunning", "valueType": "boolean" },
|
|
64
|
+
{ "tagName": "isAutoCycleRunning", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bAutoCycleRunning", "valueType": "boolean" },
|
|
65
|
+
{ "tagName": "shuttlePosition", "domain": "ADS", "symbolName": "MAIN.ctx.gio.axisShuttle.fPosition", "valueType": "number" },
|
|
66
|
+
{ "tagName": "pressLoad", "domain": "ADS", "symbolName": "MAIN.ctx.gm.fPressLoad", "valueType": "number" },
|
|
67
|
+
{ "tagName": "pressPosition", "domain": "ADS", "symbolName": "MAIN.ctx.gm.fPressPosition", "valueType": "number" },
|
|
68
|
+
{ "tagName": "pressPeakLoad", "domain": "ADS", "symbolName": "MAIN.ctx.gm.fPressPeakLoad", "valueType": "number" },
|
|
69
|
+
{ "tagName": "isReadyForCycle", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bReadyForCycle", "valueType": "boolean" },
|
|
70
|
+
{ "tagName": "reqStartAuto", "domain": "ADS", "symbolName": "MAIN.ctx.gsig.bSigStartAuto", "valueType": "boolean" },
|
|
71
|
+
{ "tagName": "useSecondaryLoadChannel", "domain": "ADS", "symbolName": "MAIN.ctx.gnv.bEnableSecondaryLoadInput", "valueType": "boolean" },
|
|
72
|
+
{ "tagName": "isLoadCellOverrange", "domain": "ADS", "symbolName": "MAIN.ctx.gio.xstLoadInputStatus.IsOverrange", "valueType": "boolean" },
|
|
73
|
+
{ "tagName": "isLoadCellUnderrange", "domain": "ADS", "symbolName": "MAIN.ctx.gio.xstLoadInputStatus.IsUnderrange", "valueType": "boolean" },
|
|
74
|
+
{ "tagName": "isMotorsOn", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bAllMotorsOn", "valueType": "boolean" },
|
|
75
|
+
{ "tagName": "isAxisErrorPresent", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bAxisErrorPresent", "valueType": "boolean" },
|
|
76
|
+
{ "tagName": "isAllAxesHomed", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bAllAxesHomed", "valueType": "boolean" },
|
|
77
|
+
{ "tagName": "isReadyForOperation", "domain": "ADS", "symbolName": "MAIN.ctx.gm.bReadyForOperation", "valueType": "boolean" }
|
|
78
|
+
] as const satisfies readonly TagConfig[];
|
|
79
|
+
|
|
80
|
+
export default acTagSpec;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### AutoCore.ts
|
|
84
|
+
|
|
85
|
+
Once the tags are created, we create a simple TypeScript file to make and provide the hooks throughout the application. By producing typed hooks, Intellisense is able to provide support for tag names and value types.
|
|
86
|
+
|
|
87
|
+
Create AutoCore.ts in the same directory as App.tsx and AutoCoreTags.ts. The template below should work for any project.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
/**
|
|
91
|
+
* @module src/AutoCore
|
|
92
|
+
* @preferred
|
|
93
|
+
*
|
|
94
|
+
* Strongly-typed hooks for AutoCore tags.
|
|
95
|
+
* Could be automatically generated by autocore-react plugin.
|
|
96
|
+
*
|
|
97
|
+
* This module binds your app’s tag spec (from `tags.json` or `tags-spec.ts`) to the
|
|
98
|
+
* global `AutoCoreTagContext`, producing **typed hooks** that provide perfect
|
|
99
|
+
* IntelliSense for tag names and value types.
|
|
100
|
+
*
|
|
101
|
+
* @remarks
|
|
102
|
+
* - Pair this with the runtime provider in your app root:
|
|
103
|
+
* `AutoCoreTagProvider` wraps `<App/>` and receives the same `spec`.
|
|
104
|
+
* - These hooks expose:
|
|
105
|
+
* - `useAutoCoreTag(tagName)` → `{ value, write, tap, isLoading, ... }`
|
|
106
|
+
* - `useAutoCoreTags(tagNames[])` → `{ values, set, isLoading }`
|
|
107
|
+
* - `useAutoCoreSelect(selector)` → `{ selected, isLoading }`
|
|
108
|
+
* - Keep `tags.json` (or `tags.ts`) as the single source of truth. If using JSON,
|
|
109
|
+
* validate and narrow it before export so `valueType` is the literal union
|
|
110
|
+
* `"boolean" | "number" | "string" | "json"`.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* // src/AutoCore.ts
|
|
115
|
+
* import { AutoCoreTagContext } from "@adcops/autocore-react/core/AutoCoreTagContext";
|
|
116
|
+
* import { makeAutoCoreTagHooks } from "@adcops/autocore-react/hooks/useAutoCoreTag";
|
|
117
|
+
* import { spec } from "./tags-spec"; // wraps ./tags.json and exports a narrowed, validated spec
|
|
118
|
+
*
|
|
119
|
+
* export const AutoCoreHooks = makeAutoCoreTagHooks(AutoCoreTagContext, spec);
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```tsx
|
|
124
|
+
* // App.tsx (runtime provider)
|
|
125
|
+
* import rawSpec from "./tags.json" assert { type: "json" };
|
|
126
|
+
* import { AutoCoreTagProvider } from "@adcops/autocore-react/core/AutoCoreTagContext";
|
|
127
|
+
*
|
|
128
|
+
* <EventEmitterProvider>
|
|
129
|
+
* <AutoCoreTagProvider tags={rawSpec} eagerRead>
|
|
130
|
+
* <App/>
|
|
131
|
+
* </AutoCoreTagProvider>
|
|
132
|
+
* </EventEmitterProvider>
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```tsx
|
|
137
|
+
* // Any component
|
|
138
|
+
* import { AutoCoreHooks } from "./AutoCore";
|
|
139
|
+
*
|
|
140
|
+
* const { value: solForward, tap } = AutoCoreHooks.useAutoCoreTag("gio.solForward");
|
|
141
|
+
* const { value: preset, write } = AutoCoreHooks.useAutoCoreTag("gm.ton.tPreset");
|
|
142
|
+
*
|
|
143
|
+
* const { values } = AutoCoreHooks.useAutoCoreTags(["isDoorClosed", "isControlPowerOk"]);
|
|
144
|
+
*
|
|
145
|
+
* const { selected: ready } = AutoCoreHooks.useAutoCoreSelect(v =>
|
|
146
|
+
* !!v["isDoorClosed"] && !!v["isControlPowerOk"]
|
|
147
|
+
* );
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* @see {@link @adcops/autocore-react/core/AutoCoreTagContext.AutoCoreTagProvider | AutoCoreTagProvider}
|
|
151
|
+
* @see {@link @adcops/autocore-react/hooks/useAutoCoreTag.makeAutoCoreTagHooks | makeAutoCoreTagHooks}
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
import {acTagSpec} from "./AutoCoreTags";
|
|
155
|
+
import { AutoCoreTagContext } from "@adcops/autocore-react/core/AutoCoreTagContext";
|
|
156
|
+
import { makeAutoCoreTagHooks } from "@adcops/autocore-react/hooks/useAutoCoreTag";
|
|
157
|
+
|
|
158
|
+
export const AutoCoreHooks = makeAutoCoreTagHooks(AutoCoreTagContext, acTagSpec);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# Providing Context to the App
|
|
163
|
+
|
|
164
|
+
With our autocore tags and hooks defined, we now wrap our app in the provided context. Make sure to wrap the application in both the EventEmitterProvider and AutoCoreTagProvider. The example shown also uses primereact.
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { PrimeReactProvider} from 'primereact/api';
|
|
169
|
+
import { EventEmitterProvider } from "@adcops/autocore-react/core/EventEmitterContext.js";
|
|
170
|
+
import {AutoCoreTagProvider} from "@adcops/autocore-react/core/AutoCoreTagContext";
|
|
171
|
+
import {acTagSpec} from "./AutoCoreTags";
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
// 3. Developer panel
|
|
175
|
+
export const AutoCorePanel = makeAutoCoreDevPanel(AutoCore.Context, spec);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# Providing Context to Your App
|
|
180
|
+
|
|
181
|
+
Wrap your application with the provider:
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
function App() {
|
|
185
|
+
return(
|
|
186
|
+
<EventEmitterProvider>
|
|
187
|
+
<PrimeReactProvider>
|
|
188
|
+
<AutoCoreTagProvider tags={acTagSpec} eagerRead>
|
|
189
|
+
<div className="app-wrapper">
|
|
190
|
+
|
|
191
|
+
<main className="main-wrapper">
|
|
192
|
+
|
|
193
|
+
<section className="content-wrapper">
|
|
194
|
+
<ContentView />
|
|
195
|
+
</section>
|
|
196
|
+
|
|
197
|
+
</main>
|
|
198
|
+
|
|
199
|
+
<footer className="footer-wrapper">
|
|
200
|
+
<FooterView />
|
|
201
|
+
</footer>
|
|
202
|
+
|
|
203
|
+
</div>
|
|
204
|
+
</AutoCoreTagProvider>
|
|
205
|
+
</PrimeReactProvider>
|
|
206
|
+
</EventEmitterProvider>
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
- `eagerRead` (default: true) triggers initial reads so values are populated immediately.
|
|
212
|
+
|
|
213
|
+
# Using the Hooks
|
|
214
|
+
|
|
215
|
+
1. useAutoCoreTag(tagName)
|
|
216
|
+
|
|
217
|
+
Access a single tag. Provides:
|
|
218
|
+
- `value` – current value.
|
|
219
|
+
- `setValue` / write – optimistic setter (updates context + backend).
|
|
220
|
+
- `writeRaw` – direct backend write, no optimistic update.
|
|
221
|
+
- `tap` – momentary true → false action (for booleans).
|
|
222
|
+
- `isLoading` – whether the initial read is pending.
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
2. useAutoCoreTags([tagNames])
|
|
226
|
+
|
|
227
|
+
Access multiple tags at once. Provides:
|
|
228
|
+
|
|
229
|
+
- `values` – object with each tag’s current value.
|
|
230
|
+
- `set(tagName, value)` – update any tag in the set.
|
|
231
|
+
- `isLoading` – whether the initial read is pending.
|
|
232
|
+
|
|
233
|
+
3. useAutoCoreSelect(selector)
|
|
234
|
+
|
|
235
|
+
Derive a computed value from the full tag state.
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
const { selected: ready } = AutoCoreHooks.useAutoCoreSelect(v =>
|
|
239
|
+
!!v["gio.solForward"] && (v["gm.ton.tPreset"] ?? 0) > 0
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
return <Button disabled={!ready}>Start</Button>;
|
|
243
|
+
```
|
|
244
|
+
- `ready` is recomputed any time gio.solForward or gm.ton.tPreset changes.
|
|
245
|
+
- Great for business logic conditions.
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# Automatic Scaling
|
|
249
|
+
|
|
250
|
+
AutoCoreTagContext supports automatic unit conversion through named scales with server synchronization. Scale factors are automatically kept in sync with autocore-server values, enabling user-configurable units that persist across sessions.
|
|
251
|
+
|
|
252
|
+
### How Scaling Works
|
|
253
|
+
|
|
254
|
+
- Incoming values: Server values are multiplied by the scale factor before being stored
|
|
255
|
+
- Outgoing values: Display values are divided by the scale factor before being sent to server
|
|
256
|
+
- Components see scaled values: All hook operations work with display-friendly values
|
|
257
|
+
- Automatic sync: Scale factors with serverTag properties are automatically synchronized with their server values.
|
|
258
|
+
- Real-time updates: Scale changes take effect immediately across the application.
|
|
259
|
+
- Consistent display: All components automatically show the same units.
|
|
260
|
+
- Dual subscriptions: The system maintains separate subscriptions for data tags and scale tags.
|
|
261
|
+
- No manual scaling needed: Eliminates the need for `useScaledValue` hooks
|
|
262
|
+
- Prevents value flicker. Values are scaled before being displayed.
|
|
263
|
+
- Type-safe. Scale names are validated at compile time.
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
Server-Driven Scales are preferred.
|
|
267
|
+
- Scale factors can come from autocore-server tags for user-configurable units.
|
|
268
|
+
- The autocore-server GNV domain will store changes in non-volatile memory.
|
|
269
|
+
- Automatic synchronization: Multiple clients stay in sync
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
## Defining Server-Driven Scales
|
|
273
|
+
|
|
274
|
+
Create AutoCoreScales.ts with server tag references:
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
import { ScaleConfig } from "@adcops/autocore-react/core/AutoCoreTagTypes";
|
|
278
|
+
|
|
279
|
+
export const acScales = {
|
|
280
|
+
position: {
|
|
281
|
+
name: "position",
|
|
282
|
+
scale: 1.0,
|
|
283
|
+
label: "mm",
|
|
284
|
+
serverTag: { domain: "MEMORYSTORE", symbolName: "position_scalar" }
|
|
285
|
+
},
|
|
286
|
+
load: {
|
|
287
|
+
name: "load",
|
|
288
|
+
scale: 1.0,
|
|
289
|
+
label: "N",
|
|
290
|
+
serverTag: { domain: "MEMORYSTORE", symbolName: "load_scalar" }
|
|
291
|
+
},
|
|
292
|
+
temperature: {
|
|
293
|
+
name: "temperature",
|
|
294
|
+
scale: 1.0,
|
|
295
|
+
label: "°C"
|
|
296
|
+
// No serverTag - static scale only
|
|
297
|
+
}
|
|
298
|
+
} as const satisfies Record<string, ScaleConfig>;
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Then reference scales in the tag definitions:
|
|
302
|
+
```tsx
|
|
303
|
+
// In AutoCoreTags.ts - add scale property to numeric tags
|
|
304
|
+
{ "tagName": "pressPosition", "domain": "ADS", "symbolName": "MAIN.ctx.gm.fPressPosition", "valueType": "number", "scale": "position" },
|
|
305
|
+
{ "tagName": "pressLoad", "domain": "ADS", "symbolName": "MAIN.ctx.gm.fPressLoad", "valueType": "number", "scale": "load" }
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
## Provider Configuration
|
|
310
|
+
|
|
311
|
+
Pass scales to the provider:
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
import { acScales } from "./AutoCoreScales";
|
|
315
|
+
|
|
316
|
+
<AutoCoreTagProvider tags={acTagSpec} scales={acScales} eagerRead>
|
|
317
|
+
<App/>
|
|
318
|
+
</AutoCoreTagProvider>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
## Updating Scale Factors
|
|
323
|
+
|
|
324
|
+
Use the updateScale function to change units at runtime.
|
|
325
|
+
|
|
326
|
+
`updateScale(scaleName, newScale, newLabel)`
|
|
327
|
+
|
|
328
|
+
Always provide both scale factor and label when calling `updateScale`.
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
Example:
|
|
332
|
+
```tsx
|
|
333
|
+
import { AutoCoreHooks } from "./AutoCore";
|
|
334
|
+
|
|
335
|
+
const UnitControls: React.FC = () => {
|
|
336
|
+
const { updateScale, getScale } = AutoCoreHooks.useScales();
|
|
337
|
+
|
|
338
|
+
const switchToMetric = async () => {
|
|
339
|
+
await updateScale("position", 1.0, "mm");
|
|
340
|
+
await updateScale("load", 1.0, "N");
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const switchToImperial = async () => {
|
|
344
|
+
await updateScale("position", 1/25.4, "in");
|
|
345
|
+
await updateScale("load", 0.224809, "lbs");
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
return (
|
|
349
|
+
<div>
|
|
350
|
+
<button onClick={switchToMetric}>Metric Units</button>
|
|
351
|
+
<button onClick={switchToImperial}>Imperial Units</button>
|
|
352
|
+
</div>
|
|
353
|
+
);
|
|
354
|
+
};
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Backward Compatibility
|
|
358
|
+
The system handles legacy installations where only numeric scale factors were stored. When legacy numeric data is received from the server, the label is set to "---" to indicate that proper units should be configured.
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
const positionScale = getScale("position");
|
|
362
|
+
if (positionScale?.label === "---") {
|
|
363
|
+
// Legacy data detected - prompt user to set proper units
|
|
364
|
+
return <div>Please configure display units in settings</div>;
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Server Data Format
|
|
369
|
+
Scale data is stored on the server as JSON objects containing both scale factor and display label.
|
|
370
|
+
|
|
371
|
+
```json
|
|
372
|
+
{
|
|
373
|
+
"scale": 1.0,
|
|
374
|
+
"label": "mm"
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
# Passing Tag Hooks as Component Props
|
|
379
|
+
When building reusable components that work with AutoCore tags, you can pass entire tag hook results as props. This provides the component with both the current value and the ability to write/tap the tag.
|
|
380
|
+
|
|
381
|
+
Use ReturnType<typeof AutoCoreHooks.useAutoCoreTag> to properly type tag hook props.
|
|
382
|
+
|
|
383
|
+
```tsx
|
|
384
|
+
interface LoadChannelSettingsProps {
|
|
385
|
+
capacitySetTag: ReturnType<typeof AutoCoreHooks.useAutoCoreTag>;
|
|
386
|
+
enabledTag: ReturnType<typeof AutoCoreHooks.useAutoCoreTag>;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const LoadChannelSettings: React.FC<LoadChannelSettingsProps> = ({
|
|
390
|
+
capacitySetTag,
|
|
391
|
+
enabledTag
|
|
392
|
+
}) => {
|
|
393
|
+
const { value: capacity, write: setCapacity } = capacitySetTag;
|
|
394
|
+
const { value: enabled, tap: toggleEnabled } = enabledTag;
|
|
395
|
+
|
|
396
|
+
return (
|
|
397
|
+
<div>
|
|
398
|
+
<input
|
|
399
|
+
type="number"
|
|
400
|
+
value={capacity ?? 0}
|
|
401
|
+
onChange={e => setCapacity(Number(e.target.value))}
|
|
402
|
+
/>
|
|
403
|
+
<button onClick={toggleEnabled}>
|
|
404
|
+
{enabled ? "Disable" : "Enable"}
|
|
405
|
+
</button>
|
|
406
|
+
</div>
|
|
407
|
+
);
|
|
408
|
+
};
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Usage
|
|
412
|
+
|
|
413
|
+
Pass the complete hook result to the component.
|
|
414
|
+
|
|
415
|
+
```tsx
|
|
416
|
+
<LoadChannelSettings
|
|
417
|
+
capacitySetTag={AutoCoreHooks.useAutoCoreTag("loadCellCapacitySet")}
|
|
418
|
+
enabledTag={AutoCoreHooks.useAutoCoreTag("loadChannelEnabled")}
|
|
419
|
+
/>
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
# DevPanel
|
|
423
|
+
For debugging, include the Dev Panel in your app:
|
|
424
|
+
|
|
425
|
+
```tsx
|
|
426
|
+
<AutoCorePanel className="fixed bottom-4 right-4 bg-black/60 text-white" />
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
It shows:
|
|
430
|
+
- Each tag’s name, type, current value.
|
|
431
|
+
- Editable input fields for live writes/taps.
|
|
432
|
+
- Loading/live status indicator.
|
|
433
|
+
|
|
434
|
+
# Best Practices
|
|
435
|
+
|
|
436
|
+
- Always define your tags in one spec to keep types consistent across your app.
|
|
437
|
+
- Prefer write (optimistic) for user-driven updates; use writeRaw only if you need backend confirmation before updating UI.
|
|
438
|
+
- Use tap only for boolean momentary actions (reset, trigger, etc.).
|
|
439
|
+
- Use useAutoCoreSelect to express high-level conditions (isSafeToStart, readyToCycle) instead of scattering logic through components.
|
|
440
|
+
- Keep the Dev Panel available in development to quickly test and validate your tag interactions.
|
|
441
|
+
|