@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.
Files changed (215) hide show
  1. package/LICENSE +58 -58
  2. package/additional-docs/AutoCoreTagContext.md +441 -441
  3. package/additional-docs/ButtonApiSpecs.md +48 -48
  4. package/additional-docs/GlobalEventEmitter.md +243 -243
  5. package/additional-docs/general_recommendations.md +22 -22
  6. package/additional-docs/react_performance_notes.md +94 -94
  7. package/dist/assets/svg/blockly_logo.svg +82 -82
  8. package/dist/assets/svg/distance.svg +40 -40
  9. package/dist/assets/svg/python_logo.svg +246 -246
  10. package/dist/assets/svg/rotation_ccw.svg +50 -50
  11. package/dist/assets/svg/rotation_ccw_a.svg +57 -57
  12. package/dist/assets/svg/rotation_ccw_b.svg +57 -57
  13. package/dist/assets/svg/rotation_ccw_c.svg +57 -57
  14. package/dist/assets/svg/rotation_cw.svg +49 -49
  15. package/dist/assets/svg/rotation_cw_a.svg +30 -30
  16. package/dist/assets/svg/rotation_cw_b.svg +30 -30
  17. package/dist/assets/svg/rotation_cw_c.svg +30 -30
  18. package/dist/assets/svg/speed.svg +39 -39
  19. package/dist/components/BlocklyEditor.css +93 -93
  20. package/dist/components/JogPanel.css +41 -41
  21. package/dist/components/ProgressBarWithValue.css +27 -27
  22. package/dist/components/ValueIndicator.css +31 -31
  23. package/dist/components/osk.css +123 -123
  24. package/dist/core/AutoCoreTagContext.d.ts.map +1 -1
  25. package/dist/core/AutoCoreTagContext.js +1 -1
  26. package/dist/hub/HubBase.d.ts +3 -3
  27. package/dist/hub/HubBase.d.ts.map +1 -1
  28. package/dist/hub/HubBase.js +1 -1
  29. package/package.json +104 -104
  30. package/readme.md +343 -343
  31. package/src/assets/BlocklyLogo.tsx +27 -27
  32. package/src/assets/Distance.tsx +18 -18
  33. package/src/assets/JogLong.tsx +13 -13
  34. package/src/assets/JogMedium.tsx +13 -13
  35. package/src/assets/JogShort.tsx +13 -13
  36. package/src/assets/PythonLogo.tsx +83 -83
  37. package/src/assets/Rotation3D.tsx +13 -13
  38. package/src/assets/RotationCcw.tsx +33 -33
  39. package/src/assets/RotationCcwA.tsx +45 -45
  40. package/src/assets/RotationCcwB.tsx +45 -45
  41. package/src/assets/RotationCcwC.tsx +45 -45
  42. package/src/assets/RotationCw.tsx +31 -31
  43. package/src/assets/RotationCwA.tsx +42 -42
  44. package/src/assets/RotationCwB.tsx +42 -42
  45. package/src/assets/RotationCwC.tsx +42 -42
  46. package/src/assets/Run.tsx +13 -13
  47. package/src/assets/Speed.tsx +18 -18
  48. package/src/assets/SpeedFast.tsx +13 -13
  49. package/src/assets/SpeedMedium.tsx +13 -13
  50. package/src/assets/SpeedNone.tsx +13 -13
  51. package/src/assets/SpeedSlow.tsx +13 -13
  52. package/src/assets/Walk.tsx +13 -13
  53. package/src/assets/index.ts +22 -22
  54. package/src/assets/svg/blockly_logo.svg +82 -82
  55. package/src/assets/svg/distance.svg +40 -40
  56. package/src/assets/svg/python_logo.svg +246 -246
  57. package/src/assets/svg/rotation_ccw.svg +50 -50
  58. package/src/assets/svg/rotation_ccw_a.svg +57 -57
  59. package/src/assets/svg/rotation_ccw_b.svg +57 -57
  60. package/src/assets/svg/rotation_ccw_c.svg +57 -57
  61. package/src/assets/svg/rotation_cw.svg +49 -49
  62. package/src/assets/svg/rotation_cw_a.svg +30 -30
  63. package/src/assets/svg/rotation_cw_b.svg +30 -30
  64. package/src/assets/svg/rotation_cw_c.svg +30 -30
  65. package/src/assets/svg/speed.svg +39 -39
  66. package/src/components/AutoCoreDevPanel.tsx +414 -414
  67. package/src/components/BlocklyEditor.css +93 -93
  68. package/src/components/BlocklyEditor.tsx +609 -609
  69. package/src/components/CodeEditor.tsx +155 -155
  70. package/src/components/FileList.tsx +390 -390
  71. package/src/components/FileSelect.tsx +128 -128
  72. package/src/components/FitText.tsx +35 -35
  73. package/src/components/Indicator.tsx +188 -188
  74. package/src/components/IndicatorButton.tsx +214 -214
  75. package/src/components/IndicatorRect.tsx +172 -172
  76. package/src/components/JogPanel.css +41 -41
  77. package/src/components/JogPanel.tsx +461 -461
  78. package/src/components/Lamp.tsx +243 -243
  79. package/src/components/Osk.tsx +192 -192
  80. package/src/components/OskDialog.tsx +164 -164
  81. package/src/components/ProgressBarWithValue.css +27 -27
  82. package/src/components/ProgressBarWithValue.tsx +48 -48
  83. package/src/components/TextInput.tsx +195 -195
  84. package/src/components/ToggleGroup.tsx +322 -322
  85. package/src/components/ValueDisplay.tsx +236 -236
  86. package/src/components/ValueIndicator.css +31 -31
  87. package/src/components/ValueIndicator.tsx +135 -135
  88. package/src/components/ValueInput.tsx +368 -368
  89. package/src/components/osk.css +123 -123
  90. package/src/core/ActionMode.ts +19 -19
  91. package/src/core/AutoCoreTagContext.tsx +625 -614
  92. package/src/core/AutoCoreTagTypes.ts +334 -334
  93. package/src/core/CoreStreamTypes.ts +512 -512
  94. package/src/core/EventEmitterContext.tsx +434 -434
  95. package/src/core/IndicatorButtonState.ts +34 -34
  96. package/src/core/IndicatorColor.ts +35 -35
  97. package/src/core/MaskPatterns.ts +87 -87
  98. package/src/core/NumerableTypes.ts +80 -80
  99. package/src/core/PositionContext.ts +59 -59
  100. package/src/core/UniqueId.ts +41 -41
  101. package/src/core/ValueSimulator.ts +166 -166
  102. package/src/core/hoc.tsx +65 -65
  103. package/src/hooks/adsHooks.tsx +287 -287
  104. package/src/hooks/commandHooks.tsx +300 -300
  105. package/src/hooks/index.ts +12 -12
  106. package/src/hooks/useAutoCoreTag.ts +103 -103
  107. package/src/hooks/useScaledValue.tsx +99 -99
  108. package/src/hub/CommandMessage.ts +89 -89
  109. package/src/hub/DebugPanel.ts +307 -307
  110. package/src/hub/HubBase.ts +249 -236
  111. package/src/hub/HubSimulate.ts +124 -124
  112. package/src/hub/HubTauri.ts +140 -140
  113. package/src/hub/HubWebSocket.ts +250 -250
  114. package/src/hub/debug.ts +211 -211
  115. package/src/hub/index.ts +81 -81
  116. package/src/themes/adc-dark/_extensions.scss +166 -166
  117. package/src/themes/adc-dark/_variables.scss +913 -913
  118. package/src/themes/adc-dark/blue/_fonts.scss +23 -23
  119. package/src/themes/adc-dark/blue/adc_theme.scss +31 -31
  120. package/src/themes/adc-dark/blue/theme.scss +14 -14
  121. package/src/themes/theme-base/_colors.scss +17 -17
  122. package/src/themes/theme-base/_common.scss +74 -74
  123. package/src/themes/theme-base/_components.scss +111 -111
  124. package/src/themes/theme-base/_mixins.scss +243 -243
  125. package/src/themes/theme-base/components/button/_button.scss +644 -644
  126. package/src/themes/theme-base/components/button/_speeddial.scss +91 -91
  127. package/src/themes/theme-base/components/button/_splitbutton.scss +358 -358
  128. package/src/themes/theme-base/components/data/_carousel.scss +39 -39
  129. package/src/themes/theme-base/components/data/_datascroller.scss +47 -47
  130. package/src/themes/theme-base/components/data/_datatable.scss +388 -388
  131. package/src/themes/theme-base/components/data/_dataview.scss +47 -47
  132. package/src/themes/theme-base/components/data/_filter.scss +137 -137
  133. package/src/themes/theme-base/components/data/_orderlist.scss +86 -86
  134. package/src/themes/theme-base/components/data/_organizationchart.scss +50 -50
  135. package/src/themes/theme-base/components/data/_paginator.scss +91 -91
  136. package/src/themes/theme-base/components/data/_picklist.scss +73 -73
  137. package/src/themes/theme-base/components/data/_timeline.scss +38 -38
  138. package/src/themes/theme-base/components/data/_tree.scss +184 -184
  139. package/src/themes/theme-base/components/data/_treetable.scss +431 -431
  140. package/src/themes/theme-base/components/file/_fileupload.scss +41 -41
  141. package/src/themes/theme-base/components/input/_autocomplete.scss +94 -94
  142. package/src/themes/theme-base/components/input/_calendar.scss +251 -251
  143. package/src/themes/theme-base/components/input/_cascadeselect.scss +107 -107
  144. package/src/themes/theme-base/components/input/_checkbox.scss +181 -181
  145. package/src/themes/theme-base/components/input/_chips.scss +102 -102
  146. package/src/themes/theme-base/components/input/_colorpicker.scss +17 -17
  147. package/src/themes/theme-base/components/input/_dropdown.scss +252 -252
  148. package/src/themes/theme-base/components/input/_editor.scss +122 -122
  149. package/src/themes/theme-base/components/input/_iconfield.scss +9 -9
  150. package/src/themes/theme-base/components/input/_inputgroup.scss +74 -74
  151. package/src/themes/theme-base/components/input/_inputicon.scss +14 -14
  152. package/src/themes/theme-base/components/input/_inputnumber.scss +4 -4
  153. package/src/themes/theme-base/components/input/_inputotp.scss +10 -10
  154. package/src/themes/theme-base/components/input/_inputswitch.scss +99 -99
  155. package/src/themes/theme-base/components/input/_inputtext.scss +101 -101
  156. package/src/themes/theme-base/components/input/_listbox.scss +138 -138
  157. package/src/themes/theme-base/components/input/_mention.scss +30 -30
  158. package/src/themes/theme-base/components/input/_multiselect.scss +278 -278
  159. package/src/themes/theme-base/components/input/_password.scss +32 -32
  160. package/src/themes/theme-base/components/input/_radiobutton.scss +169 -169
  161. package/src/themes/theme-base/components/input/_rating.scss +80 -80
  162. package/src/themes/theme-base/components/input/_selectbutton.scss +49 -49
  163. package/src/themes/theme-base/components/input/_slider.scss +49 -49
  164. package/src/themes/theme-base/components/input/_togglebutton.scss +99 -99
  165. package/src/themes/theme-base/components/input/_treeselect.scss +151 -151
  166. package/src/themes/theme-base/components/input/_tristatecheckbox.scss +46 -46
  167. package/src/themes/theme-base/components/menu/_breadcrumb.scss +42 -42
  168. package/src/themes/theme-base/components/menu/_contextmenu.scss +39 -39
  169. package/src/themes/theme-base/components/menu/_dock.scss +109 -109
  170. package/src/themes/theme-base/components/menu/_megamenu.scss +141 -141
  171. package/src/themes/theme-base/components/menu/_menu.scss +33 -33
  172. package/src/themes/theme-base/components/menu/_menubar.scss +216 -216
  173. package/src/themes/theme-base/components/menu/_panelmenu.scss +153 -153
  174. package/src/themes/theme-base/components/menu/_slidemenu.scss +60 -60
  175. package/src/themes/theme-base/components/menu/_steps.scss +57 -57
  176. package/src/themes/theme-base/components/menu/_tabmenu.scss +50 -50
  177. package/src/themes/theme-base/components/menu/_tieredmenu.scss +43 -43
  178. package/src/themes/theme-base/components/messages/_inlinemessage.scss +69 -69
  179. package/src/themes/theme-base/components/messages/_message.scss +107 -107
  180. package/src/themes/theme-base/components/messages/_toast.scss +100 -100
  181. package/src/themes/theme-base/components/misc/_avatar.scss +33 -33
  182. package/src/themes/theme-base/components/misc/_badge.scss +76 -76
  183. package/src/themes/theme-base/components/misc/_chip.scss +38 -38
  184. package/src/themes/theme-base/components/misc/_inplace.scss +17 -17
  185. package/src/themes/theme-base/components/misc/_metergroup.scss +80 -80
  186. package/src/themes/theme-base/components/misc/_progressbar.scss +17 -17
  187. package/src/themes/theme-base/components/misc/_scrolltop.scss +24 -24
  188. package/src/themes/theme-base/components/misc/_skeleton.scss +7 -7
  189. package/src/themes/theme-base/components/misc/_tag.scss +39 -39
  190. package/src/themes/theme-base/components/misc/_terminal.scss +12 -12
  191. package/src/themes/theme-base/components/multimedia/_galleria.scss +153 -153
  192. package/src/themes/theme-base/components/multimedia/_image.scss +53 -53
  193. package/src/themes/theme-base/components/overlay/_confirmpopup.scss +72 -72
  194. package/src/themes/theme-base/components/overlay/_dialog.scss +78 -78
  195. package/src/themes/theme-base/components/overlay/_overlaypanel.scss +64 -64
  196. package/src/themes/theme-base/components/overlay/_sidebar.scss +23 -23
  197. package/src/themes/theme-base/components/overlay/_tooltip.scss +33 -33
  198. package/src/themes/theme-base/components/panel/_accordion.scss +118 -118
  199. package/src/themes/theme-base/components/panel/_card.scss +30 -30
  200. package/src/themes/theme-base/components/panel/_divider.scss +30 -30
  201. package/src/themes/theme-base/components/panel/_fieldset.scss +47 -47
  202. package/src/themes/theme-base/components/panel/_panel.scss +47 -47
  203. package/src/themes/theme-base/components/panel/_scrollpanel.scss +10 -10
  204. package/src/themes/theme-base/components/panel/_splitter.scss +23 -23
  205. package/src/themes/theme-base/components/panel/_stepper.scss +136 -136
  206. package/src/themes/theme-base/components/panel/_tabview.scss +147 -147
  207. package/src/themes/theme-base/components/panel/_toolbar.scss +11 -11
  208. package/terser.config.cjs +25 -25
  209. package/todo.md +18 -18
  210. package/tools/build-themes.cjs +65 -65
  211. package/tools/copy-distribution-files.cjs +77 -77
  212. package/tools/minify.cjs +55 -55
  213. package/tsconfig.json +48 -48
  214. package/typedoc.json +12 -12
  215. 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
+