@adcops/autocore-react 3.3.9 → 3.3.14

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