@adcops/autocore-react 3.3.124 → 3.5.3

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 (48) hide show
  1. package/dist/components/UnitSystemSelector.d.ts +17 -0
  2. package/dist/components/UnitSystemSelector.d.ts.map +1 -0
  3. package/dist/components/UnitSystemSelector.js +1 -0
  4. package/dist/components/UnitsEditor.d.ts +11 -0
  5. package/dist/components/UnitsEditor.d.ts.map +1 -0
  6. package/dist/components/UnitsEditor.js +1 -0
  7. package/dist/components/index.d.ts +4 -0
  8. package/dist/components/index.d.ts.map +1 -1
  9. package/dist/components/index.js +1 -1
  10. package/dist/components/tis/ResultHistoryTable.d.ts.map +1 -1
  11. package/dist/components/tis/ResultHistoryTable.js +1 -1
  12. package/dist/components/tis/ScienceTable.d.ts.map +1 -1
  13. package/dist/components/tis/ScienceTable.js +1 -1
  14. package/dist/components/tis/TestDataView.d.ts +16 -4
  15. package/dist/components/tis/TestDataView.d.ts.map +1 -1
  16. package/dist/components/tis/TestDataView.js +1 -1
  17. package/dist/components/tis/TestFieldRow.d.ts +69 -7
  18. package/dist/components/tis/TestFieldRow.d.ts.map +1 -1
  19. package/dist/components/tis/TestFieldRow.js +1 -1
  20. package/dist/components/tis/TestSetupForm.d.ts.map +1 -1
  21. package/dist/components/tis/TestSetupForm.js +1 -1
  22. package/dist/components/tis/TisProvider.d.ts.map +1 -1
  23. package/dist/components/tis/TisProvider.js +1 -1
  24. package/dist/core/AutoCoreTagContext.d.ts.map +1 -1
  25. package/dist/core/AutoCoreTagContext.js +1 -1
  26. package/dist/core/AutoCoreTagTypes.d.ts +82 -7
  27. package/dist/core/AutoCoreTagTypes.d.ts.map +1 -1
  28. package/dist/core/formatScaled.d.ts +16 -0
  29. package/dist/core/formatScaled.d.ts.map +1 -0
  30. package/dist/core/formatScaled.js +1 -0
  31. package/dist/hooks/useAutoCoreTag.d.ts +11 -3
  32. package/dist/hooks/useAutoCoreTag.d.ts.map +1 -1
  33. package/dist/hooks/useAutoCoreTag.js +1 -1
  34. package/package.json +1 -1
  35. package/src/components/UnitSystemSelector.tsx +88 -0
  36. package/src/components/UnitsEditor.tsx +317 -0
  37. package/src/components/index.ts +4 -0
  38. package/src/components/tis/ResultHistoryTable.tsx +89 -6
  39. package/src/components/tis/ScienceTable.tsx +13 -0
  40. package/src/components/tis/TestDataView.tsx +153 -35
  41. package/src/components/tis/TestFieldRow.tsx +126 -19
  42. package/src/components/tis/TestSetupForm.tsx +35 -7
  43. package/src/components/tis/TisProvider.tsx +46 -3
  44. package/src/core/AutoCoreTagContext.tsx +165 -16
  45. package/src/core/AutoCoreTagTypes.ts +91 -7
  46. package/src/core/formatScaled.ts +72 -0
  47. package/src/hooks/useAutoCoreTag.ts +51 -3
  48. package/todo.md +6 -0
@@ -1 +1 @@
1
- import{jsx as _jsx}from"react/jsx-runtime";import React,{createContext,useCallback,useContext,useEffect,useMemo,useReducer,useRef,useState}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";const EMPTY_STATE={staged:!1,stagedProjectId:"",stagedMethodId:"",stagedSampleId:"",active:!1,activeProjectId:"",activeMethodId:"",activeSampleId:"",activeRunId:"",lastStartError:""},EMPTY_SELECTION={projectId:"",methodId:"",sampleId:"",runId:""},EMPTY_CYCLE_CONFIG={projectId:"",methodId:"",scope:"run",promptBeforeCycle:!1,values:{},latched:null,ready:!0,missing:[],promptDue:!1},cycleConfigFromPayload=e=>({projectId:String(e?.project_id??""),methodId:String(e?.method_id??""),scope:"cycle"===e?.scope?"cycle":"run",promptBeforeCycle:!!e?.prompt_before_cycle,values:e?.values&&"object"==typeof e.values?e.values:{},latched:e?.latched&&"object"==typeof e.latched?e.latched:null,ready:!1!==e?.ready,missing:Array.isArray(e?.missing)?e.missing:[],promptDue:!!e?.prompt_due}),TisContext=createContext({schemas:{},projectAssetRefs:[],defaultMethodId:"",schemasLoaded:!1,state:EMPTY_STATE,selection:EMPTY_SELECTION,setSelection:()=>{},existingProjects:[],projectKnown:()=>!1,refreshProjects:async()=>{},markProjectJustCreated:()=>{},projectFields:{},projectFieldsLoaded:!1,loadProjectFields:async()=>null,setProjectFields:()=>{},stagedConfig:{},setStagedConfig:()=>{},clearStagedConfig:()=>{},defaultsAppliedForMethod:"",markDefaultsAppliedForMethod:()=>{},configurationName:"",setConfigurationName:()=>{},cycleConfig:EMPTY_CYCLE_CONFIG,cycleConfigFields:[],setCycleConfigValues:async()=>{},clearCycleConfig:async()=>{},refreshCycleConfig:async()=>{},fetchRuns:async()=>[],fetchRun:async()=>null,runCache:{}});function liveReducer(e,t){switch(t.kind){case"staged":return{...e,staged:t.value};case"staged_project_id":return{...e,stagedProjectId:t.value};case"staged_method_id":return{...e,stagedMethodId:t.value};case"staged_sample_id":return{...e,stagedSampleId:t.value};case"active":return{...e,active:t.value};case"active_project_id":return{...e,activeProjectId:t.value};case"active_method_id":return{...e,activeMethodId:t.value};case"active_sample_id":return{...e,activeSampleId:t.value};case"active_run_id":return{...e,activeRunId:t.value};case"last_start_error":return{...e,lastStartError:t.value}}}export const TisProvider=({children:e,defaultMethodId:t})=>{const{invoke:s,subscribe:c,unsubscribe:a}=useContext(EventEmitterContext),[r,d]=useState({}),[o,n]=useState([]),[i,u]=useState(t??""),[l,p]=useState(!1),[f,_]=useReducer(liveReducer,EMPTY_STATE),[m,C]=useState({projectId:null,methodId:null,sampleId:null,runId:null}),I=useCallback(async()=>{try{const e=await s("tis.list_schemas",MessageType.Request,{});if(e?.success&&e.data){const s=e.data.test_methods??{},c=e.data.default_method_id??"",a=Array.isArray(e.data.asset_refs)?e.data.asset_refs:[];d(s),n(a),!t&&c&&u(c),p(!0)}}catch(e){}},[s]);useEffect(()=>{I()},[I]),useEffect(()=>{const e=c("tis.schema_changed",()=>{I()});return()=>{a(e)}},[c,a,I]),useEffect(()=>{let e=!1;return(async()=>{try{const t=await s("gnv.ux.selected_project_id",MessageType.Read,{});if(e)return;const c=t?.success?t.data??"":"";"string"==typeof c&&c.length>0&&C(e=>null===e.projectId?{...e,projectId:c}:e)}catch{}})(),()=>{e=!0}},[]),useEffect(()=>{const e=[c("tis.staged",e=>_({kind:"staged",value:!!e})),c("tis.staged_project_id",e=>_({kind:"staged_project_id",value:String(e??"")})),c("tis.staged_method_id",e=>_({kind:"staged_method_id",value:String(e??"")})),c("tis.staged_sample_id",e=>_({kind:"staged_sample_id",value:String(e??"")})),c("tis.active",e=>_({kind:"active",value:!!e})),c("tis.active_project_id",e=>_({kind:"active_project_id",value:String(e??"")})),c("tis.active_method_id",e=>_({kind:"active_method_id",value:String(e??"")})),c("tis.active_sample_id",e=>_({kind:"active_sample_id",value:String(e??"")})),c("tis.active_run_id",e=>_({kind:"active_run_id",value:String(e??"")})),c("tis.last_start_error",e=>_({kind:"last_start_error",value:String(e??"")}))];return()=>{e.forEach(a)}},[c,a]);const h=useRef({}),[y,g]=useState(0),j=useCallback(()=>g(e=>e+1),[]),v=useCallback((e,t)=>{if(!e)return;const s=h.current[e]??{meta:null,cycles:[],results:{},rawData:{}};h.current[e]={...s,cycles:[...s.cycles,t]},j()},[j]),S=useCallback((e,t)=>{if(!e)return;const s=h.current[e]??{meta:null,cycles:[],results:{},rawData:{}};h.current[e]={...s,results:t},j()},[j]);useEffect(()=>{const e=c("tis.cycle_added",e=>{e?.run_id&&e.cycle&&v(e.run_id,e.cycle)}),t=c("tis.results_updated",e=>{e?.run_id&&S(e.run_id,e.results??{})});return()=>{a(e),a(t)}},[c,a,v,S]);const T=useMemo(()=>({projectId:m.projectId??f.activeProjectId,methodId:m.methodId??(f.activeMethodId||i),sampleId:m.sampleId??f.activeSampleId,runId:m.runId??f.activeRunId}),[m,f,i]),E=useCallback(e=>{C(t=>({projectId:void 0===e.projectId?t.projectId:e.projectId,methodId:void 0===e.methodId?t.methodId:e.methodId,sampleId:void 0===e.sampleId?t.sampleId:e.sampleId,runId:void 0===e.runId?t.runId:e.runId})),void 0!==e.projectId&&s("gnv.ux.selected_project_id",MessageType.Write,e.projectId??"").catch(e=>{})},[s]),x=useRef("");useEffect(()=>{const e=f.activeRunId;e&&e!==x.current&&(x.current=e,C({projectId:null,methodId:null,sampleId:null,runId:null}))},[f.activeRunId]);const M=useCallback(async(e,t)=>{if(!e)return[];const c={project_id:e};t&&(c.method_id=t);try{const e=await s("tis.list_tests",MessageType.Request,c);if(e?.success&&e.data?.tests)return e.data.tests}catch(e){}return[]},[s]),k=useCallback(async(e,t,c)=>{if(!e||!t||!c)return null;try{const a=await s("tis.read_test",MessageType.Request,{project_id:e,method_id:t,run_id:c}),r=await s("tis.read_cycles",MessageType.Request,{project_id:e,method_id:t,run_id:c,offset:0,limit:1e3,order:"asc"});if(!a?.success)return null;const d={meta:a.data??null,cycles:r?.success?r.data?.cycles??[]:[],results:a.data?.results??{},rawData:h.current[c]?.rawData??{}};return h.current[c]=d,j(),d}catch(e){return null}},[s,j]),R=useMemo(()=>({...h.current}),[y]),[P,b]=useState([]),F=useRef(new Set),[w,Y]=useState(0),[A,L]=useState({}),N=useCallback(async()=>{try{const e=await s("tis.list_projects",MessageType.Request,{});e?.success&&e.data?.projects&&b(e.data.projects)}catch(e){}},[s]),D=useCallback(e=>{e&&(F.current.has(e)||(F.current.add(e),Y(e=>e+1)))},[]),q=useCallback(e=>!!e&&(!!F.current.has(e)||P.includes(e)),[P,w]),O=useCallback((e,t)=>{e&&L(s=>({...s,[e]:t}))},[]),G=useCallback(async e=>{if(!e)return null;try{const t=await s("tis.read_project",MessageType.Request,{project_id:e});if(t?.success){const s=t.data?.project_fields??{};return L(t=>({...t,[e]:s})),s}}catch(e){}return L(t=>({...t,[e]:{}})),{}},[s]);useEffect(()=>{N()},[N]),useEffect(()=>{const e=c("tis.project_created",()=>{N()}),t=c("tis.project_updated",e=>{const t="string"==typeof e?.project_id?e.project_id:"";t&&G(t)});return()=>{a(e),a(t)}},[c,a,N,G]),useEffect(()=>{const e=T.projectId;e&&q(e)&&void 0===A[e]&&G(e)},[T.projectId,q,A,G]);const V=A[T.projectId]??{},B=void 0!==A[T.projectId],[J,K]=useState({}),W=useCallback(e=>{K(t=>({...t,...e}))},[]),[z,H]=useState(""),Q=useCallback(e=>{H(e)},[]),[U,X]=useState(""),Z=useCallback(()=>{K({}),H(""),X("")},[]),[$,ee]=useState(EMPTY_CYCLE_CONFIG),te=useMemo(()=>{const e=T.methodId?r[T.methodId]?.cycle_config_fields:void 0;return Array.isArray(e)?e:[]},[r,T.methodId]),se=useCallback(async()=>{const e=T.projectId,t=T.methodId;if(t)try{const c=await s("tis.cycle_config",MessageType.Request,{project_id:e,method_id:t});if(c?.success)return void ee(cycleConfigFromPayload(c.data));ee(EMPTY_CYCLE_CONFIG)}catch(e){ee(EMPTY_CYCLE_CONFIG)}else ee(EMPTY_CYCLE_CONFIG)},[s,T.projectId,T.methodId]),ce=useCallback(async e=>{const t=T.projectId,c=T.methodId;if(c)try{const a=await s("tis.set_cycle_config",MessageType.Request,{project_id:t,method_id:c,values:e});a?.success&&ee(cycleConfigFromPayload(a.data))}catch(e){}},[s,T.projectId,T.methodId]),ae=useCallback(async()=>{const e=T.projectId,t=T.methodId;if(t)try{const c=await s("tis.clear_cycle_config",MessageType.Request,{project_id:e,method_id:t});c?.success&&ee(cycleConfigFromPayload(c.data))}catch(e){}},[s,T.projectId,T.methodId]);useEffect(()=>{se()},[se]),useEffect(()=>{const e=c("tis.schema_changed",()=>{se()});return()=>{a(e)}},[c,a,se]),useEffect(()=>{const e=c("tis.cycle_config",e=>{const t=String(e?.method_id??"");t&&T.methodId&&t!==T.methodId||ee(cycleConfigFromPayload(e))});return()=>{a(e)}},[c,a,T.methodId]);const re=useMemo(()=>({schemas:r,projectAssetRefs:o,defaultMethodId:i,schemasLoaded:l,state:f,selection:T,setSelection:E,existingProjects:P,projectKnown:q,refreshProjects:N,markProjectJustCreated:D,projectFields:V,projectFieldsLoaded:B,loadProjectFields:G,setProjectFields:O,stagedConfig:J,setStagedConfig:W,clearStagedConfig:Z,defaultsAppliedForMethod:z,markDefaultsAppliedForMethod:Q,configurationName:U,setConfigurationName:X,cycleConfig:$,cycleConfigFields:te,setCycleConfigValues:ce,clearCycleConfig:ae,refreshCycleConfig:se,fetchRuns:M,fetchRun:k,runCache:R}),[r,o,i,l,f,T,E,P,q,N,D,V,B,G,O,J,W,Z,z,Q,U,X,$,te,ce,ae,se,M,k,R]);return _jsx(TisContext.Provider,{value:re,children:e})};export const useTis=()=>useContext(TisContext);export const useTisSchemas=()=>useContext(TisContext).schemas;export const useTisState=()=>useContext(TisContext).state;export const useTisCycleConfig=()=>{const{cycleConfig:e,cycleConfigFields:t,setCycleConfigValues:s,clearCycleConfig:c}=useContext(TisContext);return{...e,fields:t,setValues:s,clear:c}};export const useTisSelection=()=>{const{selection:e,setSelection:t}=useContext(TisContext);return[e,t]};export const useTisRuns=(e,t)=>{const{fetchRuns:s}=useContext(TisContext),[c,a]=useState([]),[r,d]=useState(!1),o=useCallback(async()=>{if(e){d(!0);try{a(await s(e,t))}finally{d(!1)}}else a([])},[e,t,s]);return useEffect(()=>{o()},[o]),{runs:c,loading:r,refresh:o}};export const useTisRun=e=>{const{selection:t,fetchRun:s,runCache:c}=useContext(TisContext),[a,r]=useState(!1),d=e??t.runId;useEffect(()=>{if(!d)return;if(c[d]?.meta)return;const e=t.projectId,a=t.methodId;e&&a&&(r(!0),s(e,a,d).finally(()=>r(!1)))},[d,t.projectId,t.methodId,s,c]);const o=d?c[d]:null;return{meta:o?.meta??null,cycles:o?.cycles??[],results:o?.results??{},rawData:o?.rawData??{},loading:a}};export{TisContext};
1
+ import{jsx as _jsx}from"react/jsx-runtime";import React,{createContext,useCallback,useContext,useEffect,useMemo,useReducer,useRef,useState}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";const EMPTY_STATE={staged:!1,stagedProjectId:"",stagedMethodId:"",stagedSampleId:"",active:!1,activeProjectId:"",activeMethodId:"",activeSampleId:"",activeRunId:"",lastStartError:""},EMPTY_SELECTION={projectId:"",methodId:"",sampleId:"",runId:""},EMPTY_CYCLE_CONFIG={projectId:"",methodId:"",scope:"run",promptBeforeCycle:!1,values:{},latched:null,ready:!0,missing:[],promptDue:!1},cycleConfigFromPayload=e=>({projectId:String(e?.project_id??""),methodId:String(e?.method_id??""),scope:"cycle"===e?.scope?"cycle":"run",promptBeforeCycle:!!e?.prompt_before_cycle,values:e?.values&&"object"==typeof e.values?e.values:{},latched:e?.latched&&"object"==typeof e.latched?e.latched:null,ready:!1!==e?.ready,missing:Array.isArray(e?.missing)?e.missing:[],promptDue:!!e?.prompt_due}),TisContext=createContext({schemas:{},projectAssetRefs:[],defaultMethodId:"",schemasLoaded:!1,state:EMPTY_STATE,selection:EMPTY_SELECTION,setSelection:()=>{},existingProjects:[],projectKnown:()=>!1,refreshProjects:async()=>{},markProjectJustCreated:()=>{},projectFields:{},projectFieldsLoaded:!1,loadProjectFields:async()=>null,setProjectFields:()=>{},stagedConfig:{},setStagedConfig:()=>{},clearStagedConfig:()=>{},defaultsAppliedForMethod:"",markDefaultsAppliedForMethod:()=>{},configurationName:"",setConfigurationName:()=>{},cycleConfig:EMPTY_CYCLE_CONFIG,cycleConfigFields:[],setCycleConfigValues:async()=>{},clearCycleConfig:async()=>{},refreshCycleConfig:async()=>{},fetchRuns:async()=>[],fetchRun:async()=>null,runCache:{}});function liveReducer(e,t){switch(t.kind){case"staged":return{...e,staged:t.value};case"staged_project_id":return{...e,stagedProjectId:t.value};case"staged_method_id":return{...e,stagedMethodId:t.value};case"staged_sample_id":return{...e,stagedSampleId:t.value};case"active":return{...e,active:t.value};case"active_project_id":return{...e,activeProjectId:t.value};case"active_method_id":return{...e,activeMethodId:t.value};case"active_sample_id":return{...e,activeSampleId:t.value};case"active_run_id":return{...e,activeRunId:t.value};case"last_start_error":return{...e,lastStartError:t.value}}}export const TisProvider=({children:e,defaultMethodId:t})=>{const{invoke:s,read:c,subscribe:a,unsubscribe:d,isConnected:r}=useContext(EventEmitterContext),[o,i]=useState({}),[n,u]=useState([]),[l,_]=useState(t??""),[f,p]=useState(!1),[m,g]=useReducer(liveReducer,EMPTY_STATE),[C,h]=useState({projectId:null,methodId:null,sampleId:null,runId:null}),y=useCallback(async()=>{try{const e=await s("tis.list_schemas",MessageType.Request,{});if(e?.success&&e.data){const s=e.data.test_methods??{},c=e.data.default_method_id??"",a=Array.isArray(e.data.asset_refs)?e.data.asset_refs:[];i(s),u(a),!t&&c&&_(c),p(!0)}}catch(e){}},[s]);useEffect(()=>{y()},[y]),useEffect(()=>{const e=a("tis.schema_changed",()=>{y()});return()=>{d(e)}},[a,d,y]),useEffect(()=>{let e=!1;return(async()=>{try{const t=await s("gnv.ux.selected_project_id",MessageType.Read,{});if(e)return;const c=t?.success?t.data??"":"";"string"==typeof c&&c.length>0&&h(e=>null===e.projectId?{...e,projectId:c}:e)}catch{}})(),()=>{e=!0}},[]),useEffect(()=>{const e=[a("tis.staged",e=>g({kind:"staged",value:!!e})),a("tis.staged_project_id",e=>g({kind:"staged_project_id",value:String(e??"")})),a("tis.staged_method_id",e=>g({kind:"staged_method_id",value:String(e??"")})),a("tis.staged_sample_id",e=>g({kind:"staged_sample_id",value:String(e??"")})),a("tis.active",e=>g({kind:"active",value:!!e})),a("tis.active_project_id",e=>g({kind:"active_project_id",value:String(e??"")})),a("tis.active_method_id",e=>g({kind:"active_method_id",value:String(e??"")})),a("tis.active_sample_id",e=>g({kind:"active_sample_id",value:String(e??"")})),a("tis.active_run_id",e=>g({kind:"active_run_id",value:String(e??"")})),a("tis.last_start_error",e=>g({kind:"last_start_error",value:String(e??"")}))],t=[{fqdn:"gm.tis_staged",kind:"staged",bool:!0},{fqdn:"gm.tis_staged_project_id",kind:"staged_project_id"},{fqdn:"gm.tis_staged_method_id",kind:"staged_method_id"},{fqdn:"gm.tis_staged_sample_id",kind:"staged_sample_id"},{fqdn:"gm.tis_active",kind:"active",bool:!0},{fqdn:"gm.tis_active_project_id",kind:"active_project_id"},{fqdn:"gm.tis_active_method_id",kind:"active_method_id"},{fqdn:"gm.tis_active_sample_id",kind:"active_sample_id"},{fqdn:"gm.tis_active_run_id",kind:"active_run_id"}];let s=!1;return(async()=>{if(r)for(const{fqdn:e,kind:a,bool:d}of t)try{const t=await c(e);if(s)return;if(t&&!1===t.success)continue;const r=t?.data;if(null==r)continue;g({kind:a,value:d?!!r:String(r)})}catch{}})(),()=>{s=!0,e.forEach(d)}},[a,d,c,r]);const I=useRef({}),[v,j]=useState(0),S=useCallback(()=>j(e=>e+1),[]),T=useCallback((e,t)=>{if(!e)return;const s=I.current[e]??{meta:null,cycles:[],results:{},rawData:{}};I.current[e]={...s,cycles:[...s.cycles,t]},S()},[S]),E=useCallback((e,t)=>{if(!e)return;const s=I.current[e]??{meta:null,cycles:[],results:{},rawData:{}};I.current[e]={...s,results:t},S()},[S]);useEffect(()=>{const e=a("tis.cycle_added",e=>{e?.run_id&&e.cycle&&T(e.run_id,e.cycle)}),t=a("tis.results_updated",e=>{e?.run_id&&E(e.run_id,e.results??{})});return()=>{d(e),d(t)}},[a,d,T,E]);const k=useMemo(()=>({projectId:C.projectId??m.activeProjectId,methodId:C.methodId??(m.activeMethodId||l),sampleId:C.sampleId??m.activeSampleId,runId:C.runId??m.activeRunId}),[C,m,l]),x=useCallback(e=>{h(t=>({projectId:void 0===e.projectId?t.projectId:e.projectId,methodId:void 0===e.methodId?t.methodId:e.methodId,sampleId:void 0===e.sampleId?t.sampleId:e.sampleId,runId:void 0===e.runId?t.runId:e.runId})),void 0!==e.projectId&&s("gnv.ux.selected_project_id",MessageType.Write,e.projectId??"").catch(e=>{})},[s]),M=useRef("");useEffect(()=>{const e=m.activeRunId;e&&e!==M.current&&(M.current=e,h({projectId:null,methodId:null,sampleId:null,runId:null}))},[m.activeRunId]);const R=useCallback(async(e,t)=>{if(!e)return[];const c={project_id:e};t&&(c.method_id=t);try{const e=await s("tis.list_tests",MessageType.Request,c);if(e?.success&&e.data?.tests)return e.data.tests}catch(e){}return[]},[s]),P=useCallback(async(e,t,c)=>{if(!e||!t||!c)return null;try{const a=await s("tis.read_test",MessageType.Request,{project_id:e,method_id:t,run_id:c}),d=await s("tis.read_cycles",MessageType.Request,{project_id:e,method_id:t,run_id:c,offset:0,limit:1e3,order:"asc"});if(!a?.success)return null;const r={meta:a.data??null,cycles:d?.success?d.data?.cycles??[]:[],results:a.data?.results??{},rawData:I.current[c]?.rawData??{}};return I.current[c]=r,S(),r}catch(e){return null}},[s,S]),b=useMemo(()=>({...I.current}),[v]),[F,w]=useState([]),q=useRef(new Set),[Y,A]=useState(0),[L,N]=useState({}),D=useCallback(async()=>{try{const e=await s("tis.list_projects",MessageType.Request,{});e?.success&&e.data?.projects&&w(e.data.projects)}catch(e){}},[s]),O=useCallback(e=>{e&&(q.current.has(e)||(q.current.add(e),A(e=>e+1)))},[]),G=useCallback(e=>!!e&&(!!q.current.has(e)||F.includes(e)),[F,Y]),V=useCallback((e,t)=>{e&&N(s=>({...s,[e]:t}))},[]),B=useCallback(async e=>{if(!e)return null;try{const t=await s("tis.read_project",MessageType.Request,{project_id:e});if(t?.success){const s=t.data?.project_fields??{};return N(t=>({...t,[e]:s})),s}}catch(e){}return N(t=>({...t,[e]:{}})),{}},[s]);useEffect(()=>{D()},[D]),useEffect(()=>{const e=a("tis.project_created",()=>{D()}),t=a("tis.project_updated",e=>{const t="string"==typeof e?.project_id?e.project_id:"";t&&B(t)});return()=>{d(e),d(t)}},[a,d,D,B]),useEffect(()=>{const e=k.projectId;e&&G(e)&&void 0===L[e]&&B(e)},[k.projectId,G,L,B]);const J=L[k.projectId]??{},K=void 0!==L[k.projectId],[W,z]=useState({}),H=useCallback(e=>{z(t=>({...t,...e}))},[]),[Q,U]=useState(""),X=useCallback(e=>{U(e)},[]),[Z,$]=useState(""),ee=useCallback(()=>{z({}),U(""),$("")},[]),[te,se]=useState(EMPTY_CYCLE_CONFIG),ce=useMemo(()=>{const e=k.methodId?o[k.methodId]?.cycle_config_fields:void 0;return Array.isArray(e)?e:[]},[o,k.methodId]),ae=useCallback(async()=>{const e=k.projectId,t=k.methodId;if(t)try{const c=await s("tis.cycle_config",MessageType.Request,{project_id:e,method_id:t});if(c?.success)return void se(cycleConfigFromPayload(c.data));se(EMPTY_CYCLE_CONFIG)}catch(e){se(EMPTY_CYCLE_CONFIG)}else se(EMPTY_CYCLE_CONFIG)},[s,k.projectId,k.methodId]),de=useCallback(async e=>{const t=k.projectId,c=k.methodId;if(c)try{const a=await s("tis.set_cycle_config",MessageType.Request,{project_id:t,method_id:c,values:e});a?.success&&se(cycleConfigFromPayload(a.data))}catch(e){}},[s,k.projectId,k.methodId]),re=useCallback(async()=>{const e=k.projectId,t=k.methodId;if(t)try{const c=await s("tis.clear_cycle_config",MessageType.Request,{project_id:e,method_id:t});c?.success&&se(cycleConfigFromPayload(c.data))}catch(e){}},[s,k.projectId,k.methodId]);useEffect(()=>{ae()},[ae]),useEffect(()=>{const e=a("tis.schema_changed",()=>{ae()});return()=>{d(e)}},[a,d,ae]),useEffect(()=>{const e=a("tis.cycle_config",e=>{const t=String(e?.method_id??"");t&&k.methodId&&t!==k.methodId||se(cycleConfigFromPayload(e))});return()=>{d(e)}},[a,d,k.methodId]);const oe=useMemo(()=>({schemas:o,projectAssetRefs:n,defaultMethodId:l,schemasLoaded:f,state:m,selection:k,setSelection:x,existingProjects:F,projectKnown:G,refreshProjects:D,markProjectJustCreated:O,projectFields:J,projectFieldsLoaded:K,loadProjectFields:B,setProjectFields:V,stagedConfig:W,setStagedConfig:H,clearStagedConfig:ee,defaultsAppliedForMethod:Q,markDefaultsAppliedForMethod:X,configurationName:Z,setConfigurationName:$,cycleConfig:te,cycleConfigFields:ce,setCycleConfigValues:de,clearCycleConfig:re,refreshCycleConfig:ae,fetchRuns:R,fetchRun:P,runCache:b}),[o,n,l,f,m,k,x,F,G,D,O,J,K,B,V,W,H,ee,Q,X,Z,$,te,ce,de,re,ae,R,P,b]);return _jsx(TisContext.Provider,{value:oe,children:e})};export const useTis=()=>useContext(TisContext);export const useTisSchemas=()=>useContext(TisContext).schemas;export const useTisState=()=>useContext(TisContext).state;export const useTisCycleConfig=()=>{const{cycleConfig:e,cycleConfigFields:t,setCycleConfigValues:s,clearCycleConfig:c}=useContext(TisContext);return{...e,fields:t,setValues:s,clear:c}};export const useTisSelection=()=>{const{selection:e,setSelection:t}=useContext(TisContext);return[e,t]};export const useTisRuns=(e,t)=>{const{fetchRuns:s}=useContext(TisContext),[c,a]=useState([]),[d,r]=useState(!1),o=useCallback(async()=>{if(e){r(!0);try{a(await s(e,t))}finally{r(!1)}}else a([])},[e,t,s]);return useEffect(()=>{o()},[o]),{runs:c,loading:d,refresh:o}};export const useTisRun=e=>{const{selection:t,fetchRun:s,runCache:c}=useContext(TisContext),[a,d]=useState(!1),r=e??t.runId;useEffect(()=>{if(!r)return;if(c[r]?.meta)return;const e=t.projectId,a=t.methodId;e&&a&&(d(!0),s(e,a,r).finally(()=>d(!1)))},[r,t.projectId,t.methodId,s,c]);const o=r?c[r]:null;return{meta:o?.meta??null,cycles:o?.cycles??[],results:o?.results??{},rawData:o?.rawData??{},loading:a}};export{TisContext};
@@ -1 +1 @@
1
- {"version":3,"file":"AutoCoreTagContext.d.ts","sourceRoot":"","sources":["../../src/core/AutoCoreTagContext.tsx"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,KAAK,EAAE,EAQV,KAAK,SAAS,EACjB,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EACR,gBAAgB,EAChB,SAAS,EACT,WAAW,EACd,MAAM,oBAAoB,CAAC;AAI5B;;;GAGG;AACH,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE3C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,kBAAkB,8CAW7B,CAAC;AAsBH;;;;GAIG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC;IACvC,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,EAAE,SAAS,SAAS,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B,CA2lBA,CAAC"}
1
+ {"version":3,"file":"AutoCoreTagContext.d.ts","sourceRoot":"","sources":["../../src/core/AutoCoreTagContext.tsx"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,KAAK,EAAE,EAQV,KAAK,SAAS,EACjB,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EACR,gBAAgB,EAChB,SAAS,EACT,WAAW,EAGd,MAAM,oBAAoB,CAAC;AAK5B;;;GAGG;AACH,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE3C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,kBAAkB,8CAW7B,CAAC;AAsBH;;;;GAIG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC;IACvC,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,EAAE,SAAS,SAAS,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B,CA6uBA,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx}from"react/jsx-runtime";import React,{useRef,createContext,useCallback,useContext,useEffect,useMemo,useState}from"react";import{EventEmitterContext}from"./EventEmitterContext";import{MessageType}from"../hub/CommandMessage";export const AutoCoreTagContext=createContext({values:{},rawValues:{},isLoading:!0,write:async()=>{},tap:async()=>{},press:async()=>{},release:async()=>{},scales:{},updateScale:async()=>{},findTagByFqdn:()=>{}});const sleep=e=>new Promise(t=>setTimeout(t,e));export const AutoCoreTagProvider=({children:e,tags:t,scales:a,eagerRead:s=!0,flushIntervalMs:n=33})=>{const r=useRef(!1),c=useMemo(()=>({}),[]),o=a??c,{invoke:u,read:l,write:i,serverSubscribe:f,isConnected:m,subscribe:y,unsubscribe:b}=useContext(EventEmitterContext),[d,p]=useState(()=>{const e={};for(const a of t)void 0!==a.initialValue&&(e[a.tagName]=a.initialValue);return e}),[g,v]=useState({}),[C,N]=useState(o),h=useRef(C),w=useRef(d);useEffect(()=>{h.current=C},[C]),useEffect(()=>{w.current=d},[d]);const[T,M]=useState(!0),x=useCallback((e,t)=>{const{valueType:a,scale:s,codec:n}=e;if("number"===a&&"number"==typeof t&&s){const e=h.current[s];return t*(e?.scale??1)}if("json"===a&&n?.fromServer)try{return n.fromServer(t)}catch{}return t},[]),E=useCallback((e,t)=>{const{valueType:a,scale:s,codec:n}=e;if("json"===a&&n?.toServer)try{t=n.toServer(t)}catch{}if("number"===a&&"number"==typeof t&&s){const e=h.current[s];return t/(e?.scale??1)}return t},[]),k=useCallback(e=>{const a=t.filter(t=>t.scale===e);a.length&&v(e=>{const t={...e};for(const e of a){const a=w.current[e.tagName];if("number"!=typeof a)continue;const s=x(e,a);t[e.tagName]!==s&&(t[e.tagName]=s)}return t})},[t,x]),q=useRef(new Map),S=useRef(new Map),j=useRef(null),R=useCallback(()=>{j.current=null;const e=q.current,t=S.current;0===e.size&&0===t.size||(q.current=new Map,S.current=new Map,p(t=>{let a=t;return e.forEach((e,s)=>{a[s]!==e&&(a===t&&(a={...t}),a[s]=e)}),a}),v(e=>{let a=e;return t.forEach((t,s)=>{a[s]!==t&&(a===e&&(a={...e}),a[s]=t)}),a}))},[]),$=useCallback((e,t)=>{const a=x(e,t);if(n<=0)return p(a=>a[e.tagName]===t?a:{...a,[e.tagName]:t}),void v(t=>t[e.tagName]===a?t:{...t,[e.tagName]:a});q.current.set(e.tagName,t),S.current.set(e.tagName,a),null===j.current&&(j.current=setTimeout(R,n))},[x,n,R]);useEffect(()=>()=>{null!==j.current&&(clearTimeout(j.current),j.current=null),q.current=new Map,S.current=new Map},[]);const V=useCallback(async(e,t,a)=>{const s=a?.concurrency??4,n=a?.minDelayMs??20,r=a?.jitterMs??40;let c=0;const o=Array.from({length:s},()=>(async()=>{for(;;){const a=c++;if(a>=t.length)return;const s=t[a];try{let t=!1;try{await u(s.fqdn,MessageType.Request,{action:"refresh"}),t=!0}catch{}if(!t){const t="GNV"===e.toUpperCase()?{group:"ux"}:{};try{const e=await l(s.fqdn,t);e?.success&&$(s,e.data)}catch(e){}}}catch(e){}finally{const e=Math.floor(Math.random()*r);await sleep(n+e)}}})());await Promise.all(o)},[u,l,$]),A=useCallback(async()=>{for(const[e,t]of Object.entries(o)){if(!t.serverTag)continue;const{domain:a,symbolName:s}=t.serverTag;try{const t=await l(`${a}.${s}`,{group:"ux"});if(t.data&&(t.success??1)&&(t.valid??1)){const a=JSON.parse(t.data);if(a&&"number"==typeof a.scale){const{scale:t,label:s}=a;N(a=>({...a,[e]:{...a[e],scale:t,label:s??a[e]?.label??"---"}})),k(e)}}}catch{}}},[l,o,k]);useEffect(()=>{let e=!0;const a=[],n=async()=>{if(e&&!r.current){r.current=!0;try{await(async()=>{try{await A();for(const n of t){await f(n.tagName,n.fqdn,n.subscriptionOptions);const t=y(n.tagName,t=>{e&&$(n,t)});a.push(t),s&&l(n.fqdn).then(t=>{e&&t?.success&&$(n,t.data)}).catch(e=>{})}}finally{e&&setTimeout(()=>e&&M(!1),100)}})()}finally{e&&setTimeout(()=>e&&M(!1),100)}}};if(m())n();else{const e=y("HUB/connected",()=>{b(e),n()});a.push(e)}return()=>{e=!1,a.forEach(b),r.current=!1}},[y,b,m,u,f,s,t,A,V,$]);const O=useMemo(()=>Object.entries(o).filter(([,e])=>e.serverTag).map(([e,t])=>({scaleName:e,domain:t.serverTag.domain,symbolName:t.serverTag.symbolName})),[o]);useEffect(()=>{let e=!0;const t=[];for(const{scaleName:a,domain:s,symbolName:n}of O){const r=y(`${s}.${n}`,t=>{if(!e)return;const s=t?.value;if(s&&"object"==typeof s&&"number"==typeof s.scale){const{scale:e,label:t}=s;N(s=>({...s,[a]:{...s[a],scale:e,label:t??s[a]?.label??"---"}})),k(a)}});t.push(r)}return()=>{e=!1,t.forEach(b)}},[y,b,O,k]);const P=useCallback(async(e,a)=>{const s=t.find(t=>t.tagName===e);if(!s)return;const n=E(s,a);await i(s.fqdn,n)},[t,i,E]),B=useCallback(async e=>{const a=t.find(t=>t.tagName===e);a&&"boolean"===a.valueType&&(i(a.fqdn,!0).catch(e=>{}),await sleep(300),i(a.fqdn,!1).catch(e=>{}))},[t,i]),z=useCallback(async e=>{const a=t.find(t=>t.tagName===e);a&&"boolean"===a.valueType&&await i(a.fqdn,!0)},[t,i]),F=useCallback(async e=>{const a=t.find(t=>t.tagName===e);a&&"boolean"===a.valueType&&await i(a.fqdn,!1)},[t,i]),L=useCallback(async(e,t,a)=>{const s=C[e];if(s){if(s.serverTag){const n=`${s.serverTag.domain}.${s.serverTag.symbolName}`;await i(n,{name:e,scale:t,label:a})}N(s=>({...s,[e]:{...s[e],scale:t,label:a}})),k(e)}},[C,i,k]);useEffect(()=>{v(e=>{const a={...e};for(const e of t){const t=w.current[e.tagName];if(void 0===t)continue;const s=x(e,t);a[e.tagName]!==s&&(a[e.tagName]=s)}return a})},[t,x,C]);const U=useMemo(()=>{const e=new Map;for(const a of t)e.set(a.fqdn,a);return e},[t]),_=useCallback(e=>U.get(e),[U]),D=useMemo(()=>({values:g,rawValues:d,isLoading:T,write:P,tap:B,press:z,release:F,scales:C,updateScale:L,findTagByFqdn:_}),[g,d,T,P,B,z,F,C,L,_]);return _jsx(AutoCoreTagContext.Provider,{value:D,children:e})};
1
+ import{jsx as _jsx}from"react/jsx-runtime";import React,{useRef,createContext,useCallback,useContext,useEffect,useMemo,useState}from"react";import{EventEmitterContext}from"./EventEmitterContext";import{MessageType}from"../hub/CommandMessage";import{formatScaled}from"./formatScaled";export const AutoCoreTagContext=createContext({values:{},rawValues:{},isLoading:!0,write:async()=>{},tap:async()=>{},press:async()=>{},release:async()=>{},scales:{},updateScale:async()=>{},findTagByFqdn:()=>{}});const sleep=e=>new Promise(t=>setTimeout(t,e));export const AutoCoreTagProvider=({children:e,tags:t,scales:a,eagerRead:s=!0,flushIntervalMs:n=33})=>{const c=useRef(!1),r=useMemo(()=>({}),[]),o=a??r,{invoke:u,read:l,write:i,serverSubscribe:f,isConnected:m,subscribe:y,unsubscribe:b}=useContext(EventEmitterContext),[d,p]=useState(()=>{const e={};for(const a of t)void 0!==a.initialValue&&(e[a.tagName]=a.initialValue);return e}),[g,v]=useState({}),[C,N]=useState(o),h=useRef(C),w=useRef(d);useEffect(()=>{h.current=C},[C]),useEffect(()=>{w.current=d},[d]);const[T,M]=useState(!0),[k,S]=useState(null),[E,q]=useState(null),x=useRef(null),R=useRef(null);useEffect(()=>{x.current=E},[E]),useEffect(()=>{R.current=k},[k]);const j=useCallback((e,t)=>{const a={};for(const[s,n]of Object.entries(e.scales??{})){const e=n?.[t];e&&"number"==typeof e.scalar&&(a[s]={name:s,scale:e.scalar,label:e.label??"",offset:"absolute"===n?.kind?e.offset??0:0,precision:e.precision,precisionMode:e.precision_mode})}return a},[]),_=useCallback((e,a)=>{S(e),q(a);const s=j(e,a);h.current=s,N(s),v(e=>{const a={...e};for(const e of t){const t=e.quantity??e.scale;if(!t||!s[t])continue;const n=w.current[e.tagName];if("number"!=typeof n)continue;const c=s[t];a[e.tagName]=n*c.scale+(c.offset??0)}return a})},[j,t]),$=useCallback((e,t)=>{const{valueType:a,codec:s}=e,n=e.quantity??e.scale;if("number"===a&&"number"==typeof t&&n){const e=h.current[n];return t*(e?.scale??1)+(e?.offset??0)}if("json"===a&&s?.fromServer)try{return s.fromServer(t)}catch{}return t},[]),O=useCallback((e,t)=>{const{valueType:a,codec:s}=e,n=e.quantity??e.scale;if("json"===a&&s?.toServer)try{t=s.toServer(t)}catch{}if("number"===a&&"number"==typeof t&&n){const e=h.current[n],a=e?.scale??1;return(t-(e?.offset??0))/a}return t},[]),V=useCallback(e=>{const a=t.filter(t=>(t.quantity??t.scale)===e);a.length&&v(e=>{const t={...e};for(const e of a){const a=w.current[e.tagName];if("number"!=typeof a)continue;const s=$(e,a);t[e.tagName]!==s&&(t[e.tagName]=s)}return t})},[t,$]),A=useRef(new Map),P=useRef(new Map),B=useRef(null),F=useCallback(()=>{B.current=null;const e=A.current,t=P.current;0===e.size&&0===t.size||(A.current=new Map,P.current=new Map,p(t=>{let a=t;return e.forEach((e,s)=>{a[s]!==e&&(a===t&&(a={...t}),a[s]=e)}),a}),v(e=>{let a=e;return t.forEach((t,s)=>{a[s]!==t&&(a===e&&(a={...e}),a[s]=t)}),a}))},[]),U=useCallback((e,t)=>{const a=$(e,t);if(n<=0)return p(a=>a[e.tagName]===t?a:{...a,[e.tagName]:t}),void v(t=>t[e.tagName]===a?t:{...t,[e.tagName]:a});A.current.set(e.tagName,t),P.current.set(e.tagName,a),null===B.current&&(B.current=setTimeout(F,n))},[$,n,F]);useEffect(()=>()=>{null!==B.current&&(clearTimeout(B.current),B.current=null),A.current=new Map,P.current=new Map},[]);const z=useCallback(async(e,t,a)=>{const s=a?.concurrency??4,n=a?.minDelayMs??20,c=a?.jitterMs??40;let r=0;const o=Array.from({length:s},()=>(async()=>{for(;;){const a=r++;if(a>=t.length)return;const s=t[a];try{let t=!1;try{await u(s.fqdn,MessageType.Request,{action:"refresh"}),t=!0}catch{}if(!t){const t="GNV"===e.toUpperCase()?{group:"ux"}:{};try{const e=await l(s.fqdn,t);e?.success&&U(s,e.data)}catch(e){}}}catch(e){}finally{const e=Math.floor(Math.random()*c);await sleep(n+e)}}})());await Promise.all(o)},[u,l,U]),L=useCallback(async()=>{try{const e=await u("system.get_units",MessageType.Request,{}),t=e?.data??e,a=t?.units;if(!a||!a.scales)return;const s=t?.active_system??a.default_system;_(a,s)}catch(e){}},[u,_]),D=useCallback(async e=>{await u("system.set_unit_system",MessageType.Request,{system:e});k&&_(k,e)},[u,k,_]);useEffect(()=>{if(!m)return;const e=y("system.unit_system_changed",e=>{const t=e?.value?.system??e?.system;if(!t)return;const a=R.current;a&&t!==x.current&&_(a,t)});return()=>{b(e)}},[m,y,b,_]);const G=useCallback(async()=>{for(const[e,t]of Object.entries(o)){if(!t.serverTag)continue;const{domain:a,symbolName:s}=t.serverTag;try{const t=await l(`${a}.${s}`,{group:"ux"});if(t.data&&(t.success??1)&&(t.valid??1)){const a=JSON.parse(t.data);if(a&&"number"==typeof a.scale){const{scale:t,label:s}=a;N(a=>({...a,[e]:{...a[e],scale:t,label:s??a[e]?.label??"---"}})),V(e)}}}catch{}}},[l,o,V]);useEffect(()=>{let e=!0;const a=[],n=async()=>{if(e&&!c.current){c.current=!0;try{await(async()=>{try{await L(),await G();for(const n of t){await f(n.tagName,n.fqdn,n.subscriptionOptions);const t=y(n.tagName,t=>{e&&U(n,t)});a.push(t),s&&l(n.fqdn).then(t=>{e&&t?.success&&U(n,t.data)}).catch(e=>{})}}finally{e&&setTimeout(()=>e&&M(!1),100)}})()}finally{e&&setTimeout(()=>e&&M(!1),100)}}};if(m())n();else{const e=y("HUB/connected",()=>{b(e),n()});a.push(e)}return()=>{e=!1,a.forEach(b),c.current=!1}},[y,b,m,u,f,s,t,G,z,U]);const H=useMemo(()=>Object.entries(o).filter(([,e])=>e.serverTag).map(([e,t])=>({scaleName:e,domain:t.serverTag.domain,symbolName:t.serverTag.symbolName})),[o]);useEffect(()=>{let e=!0;const t=[];for(const{scaleName:a,domain:s,symbolName:n}of H){const c=y(`${s}.${n}`,t=>{if(!e)return;const s=t?.value;if(s&&"object"==typeof s&&"number"==typeof s.scale){const{scale:e,label:t}=s;N(s=>({...s,[a]:{...s[a],scale:e,label:t??s[a]?.label??"---"}})),V(a)}});t.push(c)}return()=>{e=!1,t.forEach(b)}},[y,b,H,V]);const I=useCallback(async(e,a)=>{const s=t.find(t=>t.tagName===e);if(!s)return;const n=O(s,a);await i(s.fqdn,n)},[t,i,O]),J=useCallback(async e=>{const a=t.find(t=>t.tagName===e);a&&"boolean"===a.valueType&&(i(a.fqdn,!0).catch(e=>{}),await sleep(300),i(a.fqdn,!1).catch(e=>{}))},[t,i]),Q=useCallback(async e=>{const a=t.find(t=>t.tagName===e);a&&"boolean"===a.valueType&&await i(a.fqdn,!0)},[t,i]),K=useCallback(async e=>{const a=t.find(t=>t.tagName===e);a&&"boolean"===a.valueType&&await i(a.fqdn,!1)},[t,i]),W=useCallback(async(e,t,a)=>{const s=C[e];if(s){if(s.serverTag){const n=`${s.serverTag.domain}.${s.serverTag.symbolName}`;await i(n,{name:e,scale:t,label:a})}N(s=>({...s,[e]:{...s[e],scale:t,label:a}})),V(e)}},[C,i,V]);useEffect(()=>{v(e=>{const a={...e};for(const e of t){const t=w.current[e.tagName];if(void 0===t)continue;const s=$(e,t);a[e.tagName]!==s&&(a[e.tagName]=s)}return a})},[t,$,C]);const X=useMemo(()=>{const e=new Map;for(const a of t)e.set(a.fqdn,a);return e},[t]),Y=useCallback(e=>X.get(e),[X]),Z=useCallback((e,t)=>{const a=C[e];return a&&Number.isFinite(t)?formatScaled(t*a.scale+(a.offset??0),a):String(t)},[C]),ee=useMemo(()=>({values:g,rawValues:d,isLoading:T,write:I,tap:J,press:Q,release:K,scales:C,updateScale:W,findTagByFqdn:Y,units:k,activeSystem:E,setUnitSystem:D,formatQuantity:Z}),[g,d,T,I,J,Q,K,C,W,Y,k,E,D,Z]);return _jsx(AutoCoreTagContext.Provider,{value:ee,children:e})};
@@ -147,17 +147,63 @@ export interface SubscriptionOptions {
147
147
  */
148
148
  args?: Record<string, unknown>;
149
149
  }
150
+ /** How a resolved scale's `precision` is interpreted. */
151
+ export type PrecisionMode = "decimals" | "significant";
150
152
  /**
151
- * Configuration for a named scale group.
152
- * Apply by setting `scale: "<name>"` on numeric tags.
153
+ * One system's display treatment of one quantity, as it appears in `units.json`.
154
+ * Mirrors `mechutil::units::SystemScale`.
155
+ */
156
+ export interface UnitSystemScale {
157
+ /** Display label ("mm", "lbf", "°C"). */
158
+ label: string;
159
+ /** `display = backend * scalar (+ offset)`. */
160
+ scalar: number;
161
+ /** Additive term — absolute quantities (temperature) only. */
162
+ offset?: number;
163
+ /** Digits, interpreted per `precision_mode`. */
164
+ precision?: number;
165
+ precision_mode?: PrecisionMode;
166
+ }
167
+ /** One quantity row of `units.json`. Mirrors `mechutil::units::Scale`. */
168
+ export interface UnitScale {
169
+ /** The unit GM/GNV actually holds on this machine. */
170
+ backend: string;
171
+ /** `"absolute"` takes the offset; `"delta"` (default) does not. */
172
+ kind?: "delta" | "absolute";
173
+ /** system name → display treatment. Flattened in the JSON. */
174
+ [system: string]: unknown;
175
+ }
176
+ /**
177
+ * The whole `units.json` document — the single definition of units for a
178
+ * machine. Fetched from `system.get_units`; see `autocore-server/UNITS_PLAN.md`.
179
+ */
180
+ export interface UnitsTable {
181
+ /** Named systems, in display order. Any names, not a fixed pair. */
182
+ systems: string[];
183
+ default_system: string;
184
+ /** quantity name → row. Quantities are DATA, not a fixed enum. */
185
+ scales: Record<string, UnitScale>;
186
+ }
187
+ /**
188
+ * A quantity resolved against the active system — what the UI actually renders
189
+ * with. Produced by the provider from `UnitsTable` + active system.
190
+ *
191
+ * `scale`/`label` keep their historical names so the provider's existing
192
+ * rescale-from-raw machinery is unchanged; `precision` is new and is what makes
193
+ * "too many decimals" configurable rather than hard-coded.
153
194
  */
154
195
  export interface ScaleConfig {
155
- /** Unique name for this scale group (e.g., "position", "load") */
196
+ /** Quantity name this resolves (e.g., "position", "force") */
156
197
  name: string;
157
198
  /** Current scale factor — incoming values ×= scale; outgoing values /= scale */
158
199
  scale: number;
159
200
  /** Units/label for display (e.g., "mm", "in", "lbs") */
160
201
  label: string;
202
+ /** Additive term for absolute quantities (temperature). Default 0. */
203
+ offset?: number;
204
+ /** Display digits. */
205
+ precision?: number;
206
+ precisionMode?: PrecisionMode;
161
207
  /** Optional description for debugging/documentation */
162
208
  description?: string;
163
209
  /**
@@ -200,10 +246,21 @@ export type TagConfig<K extends string = string, T extends PrimitiveKind = Primi
200
246
  /** Optional initial **raw** value before first server update */
201
247
  initialValue?: TagValueOf<T>;
202
248
  /**
203
- * Optional scale group for numeric tags.
204
- * If present, provider will:
205
- * - Multiply incoming numbers by the group's `scale`
206
- * - Divide outgoing numbers by the group's `scale`
249
+ * The QUANTITY this tag measures — a row name in `units.json`
250
+ * ("position", "force", "torque", …). If present, the provider will:
251
+ * - Multiply incoming numbers by the active system's `scalar` (+ offset)
252
+ * - Divide outgoing numbers back to backend units
253
+ * - Render them at the active system's `precision`
254
+ *
255
+ * Quantities are data, not an enum: a machine with a channel nobody
256
+ * anticipated just gets a new row in `units.json`.
257
+ */
258
+ quantity?: string;
259
+ /**
260
+ * @deprecated Renamed to {@link quantity} in 4.0, when hand-authored
261
+ * `acScales` was replaced by the server's `units.json`. Still read as a
262
+ * fallback so a project can be migrated without its HMI going dark, but it
263
+ * will be removed — regenerate with `acctl codegen-tags`.
207
264
  */
208
265
  scale?: string;
209
266
  /**
@@ -258,6 +315,24 @@ export type BindingOf<Spec extends readonly TagConfig[], K extends Spec[number][
258
315
  export interface BaseContextValue<VMap extends Record<string, any>> {
259
316
  /** Current app-visible tag values (already scaled/decoded). */
260
317
  values: Partial<VMap>;
318
+ /**
319
+ * The machine's units table (`units.json`), or `null` when the machine has
320
+ * none — in which case values render in backend units.
321
+ */
322
+ units?: UnitsTable | null;
323
+ /** The display system currently in effect (e.g. "Metric"). */
324
+ activeSystem?: string | null;
325
+ /**
326
+ * Switch the machine's display system. Persisted server-side and broadcast,
327
+ * so every connected client switches live with no reload.
328
+ */
329
+ setUnitSystem?: (system: string) => Promise<void>;
330
+ /**
331
+ * Format a BACKEND value for display in the active system, honouring that
332
+ * quantity's label precision. Rounding is a rendering step only — this never
333
+ * changes a stored value.
334
+ */
335
+ formatQuantity?: (quantity: string, backendValue: number) => string;
261
336
  /** Last raw (controller) values, as received (pre-scale, pre-codec). */
262
337
  rawValues: Record<string, unknown>;
263
338
  /** Whether initial reads are pending. */
@@ -1 +1 @@
1
- {"version":3,"file":"AutoCoreTagTypes.d.ts","sourceRoot":"","sources":["../../src/core/AutoCoreTagTypes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AutoCoreTagTypes.d.ts","sourceRoot":"","sources":["../../src/core/AutoCoreTagTypes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { PrecisionMode, ScaleConfig } from "./AutoCoreTagTypes";
2
+ /** Digits used when a scale declares no precision. */
3
+ export declare const DEFAULT_PRECISION = 3;
4
+ /**
5
+ * Format an already-converted (display-unit) value.
6
+ *
7
+ * Precision is a **cap, not padding**: a clean 0/1/2 axis stays "0", "1", "2"
8
+ * rather than becoming "0.000", which is what made fixed `toFixed` unusable for
9
+ * chart ticks.
10
+ */
11
+ export declare function formatValue(value: number, precision?: number, mode?: PrecisionMode): string;
12
+ /** Format a display-unit value using a resolved scale's precision settings. */
13
+ export declare function formatScaled(displayValue: number, scale?: ScaleConfig): string;
14
+ /** `"12.5 mm"` — value plus the active system's label, when there is one. */
15
+ export declare function formatScaledWithLabel(displayValue: number, scale?: ScaleConfig): string;
16
+ //# sourceMappingURL=formatScaled.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatScaled.d.ts","sourceRoot":"","sources":["../../src/core/formatScaled.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAErE,sDAAsD;AACtD,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC;;;;;;GAMG;AACH,wBAAgB,WAAW,CACvB,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAA0B,EACrC,IAAI,GAAE,aAA0B,GACjC,MAAM,CAwBR;AAED,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM,CAM9E;AAED,6EAA6E;AAC7E,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM,CAIvF"}
@@ -0,0 +1 @@
1
+ export const DEFAULT_PRECISION=3;export function formatValue(t,e=3,a="decimals"){if(!Number.isFinite(t))return String(t);let i;if("significant"===a){const a=Math.max(1,e);if(0===t)i="0";else{const e=Math.floor(Math.log10(Math.abs(t))),r=Math.max(0,a-1-e);i=t.toFixed(Math.min(r,100))}}else i=t.toFixed(Math.max(0,Math.min(e,100)));return i.includes(".")&&(i=i.replace(/0+$/,"").replace(/\.$/,""),""!==i&&"-"!==i||(i="0")),i}export function formatScaled(t,e){return formatValue(t,e?.precision??3,e?.precisionMode??"decimals")}export function formatScaledWithLabel(t,e){const a=formatScaled(t,e),i=e?.label?.trim();return i?`${a} ${i}`:a}
@@ -1,4 +1,4 @@
1
- import type { BaseContextValue, TagConfig, TagValueMap } from "../core/AutoCoreTagTypes";
1
+ import type { BaseContextValue, ScaleConfig, TagConfig, TagValueMap } from "../core/AutoCoreTagTypes";
2
2
  export declare function makeAutoCoreTagHooks<Spec extends readonly TagConfig[]>(Context: React.Context<BaseContextValue<TagValueMap<Spec>>>, tags: Spec): {
3
3
  readonly useAutoCoreTag: <K extends Spec[number]["tagName"]>(tagName: K) => {
4
4
  readonly value: TagValueMap<Spec>[K];
@@ -20,9 +20,17 @@ export declare function makeAutoCoreTagHooks<Spec extends readonly TagConfig[]>(
20
20
  readonly isLoading: boolean;
21
21
  };
22
22
  readonly useScales: () => {
23
- readonly scales: Record<string, import("../core/AutoCoreTagTypes").ScaleConfig>;
23
+ readonly scales: Record<string, ScaleConfig>;
24
24
  readonly updateScale: (scaleName: string, newScale: number, newLabel: string) => Promise<void>;
25
- readonly getScale: (scaleName: string) => import("../core/AutoCoreTagTypes").ScaleConfig;
25
+ readonly getScale: (scaleName: string) => ScaleConfig;
26
+ };
27
+ readonly useUnits: () => {
28
+ readonly units: import("../core/AutoCoreTagTypes").UnitsTable | null;
29
+ readonly activeSystem: string | null;
30
+ readonly systems: string[];
31
+ readonly setUnitSystem: ((system: string) => Promise<void>) | undefined;
32
+ readonly label: (quantity: string) => string;
33
+ readonly format: (quantity: string, backendValue: number) => string;
26
34
  };
27
35
  };
28
36
  //# sourceMappingURL=useAutoCoreTag.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAutoCoreTag.d.ts","sourceRoot":"","sources":["../../src/hooks/useAutoCoreTag.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEzF,wBAAgB,oBAAoB,CAAC,IAAI,SAAS,SAAS,SAAS,EAAE,EAClE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAC3D,IAAI,EAAE,IAAI;8BAYc,CAAC,2CAA4B,CAAC;;;;;;;;;+BA6B7B,CAAC,4CAA6B,SAAS,CAAC,EAAE;4BAO/C,CAAC;;kCAYyB,CAAC;;;iCAUpB,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,mBAAM,CAAC,KAAK,CAAC,SAAQ,GAAG,EAAE;;;;;;;uCAW3C,MAAM;;EAKtD"}
1
+ {"version":3,"file":"useAutoCoreTag.d.ts","sourceRoot":"","sources":["../../src/hooks/useAutoCoreTag.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEtG,wBAAgB,oBAAoB,CAAC,IAAI,SAAS,SAAS,SAAS,EAAE,EAClE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAC3D,IAAI,EAAE,IAAI;8BAYc,CAAC,2CAA4B,CAAC;;;;;;;;;+BA6B7B,CAAC,4CAA6B,SAAS,CAAC,EAAE;4BAO/C,CAAC;;kCAYyB,CAAC;;;iCAUpB,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,mBAAM,CAAC,KAAK,CAAC,SAAQ,GAAG,EAAE;;;;;;;uCAmBpE,MAAM,KAAG,WAAW;;;;;;;mCAoBrB,MAAM;oCAIN,MAAM,gBAAgB,MAAM;;EAqBlD"}
@@ -1 +1 @@
1
- import{useCallback,useContext,useMemo}from"react";export function makeAutoCoreTagHooks(e,a){return{useAutoCoreTag:s=>{const{values:t,rawValues:u,isLoading:o,write:r,tap:l,press:n,release:c}=useContext(e),i=(e=>a.find(a=>a.tagName===e))(s);if(!i)throw new Error(`useAutoCoreTag: unknown tagName '${String(s)}'`);const C=useCallback(e=>r(s,e),[r,s]),g=useCallback(()=>l?.(s),[l,s]),m=useCallback(()=>n?.(s),[n,s]),w=useCallback(()=>c?.(s),[c,s]);return{value:t[s],rawValue:u[s],write:C,tap:g,press:m,release:w,isLoading:o}},useAutoCoreTags:a=>{const{values:s,rawValues:t,isLoading:u,write:o}=useContext(e);return{values:useMemo(()=>Object.fromEntries(a.map(e=>[e,s[e]])),[s,a]),rawValues:useMemo(()=>Object.fromEntries(a.map(e=>[e,t[e]])),[t,a]),write:useCallback(async(e,a)=>{await o(e,a)},[o]),isLoading:u}},useAutoCoreSelect:(a,s=[])=>{const{values:t,isLoading:u}=useContext(e);return{selected:useMemo(()=>a(t),[t,...s]),isLoading:u}},useScales:()=>{const{scales:a,updateScale:s}=useContext(e),t=useCallback(e=>a[e],[a]);return{scales:a,updateScale:s,getScale:t}}}}
1
+ import{useCallback,useContext,useMemo}from"react";export function makeAutoCoreTagHooks(e,s){return{useAutoCoreTag:a=>{const{values:t,rawValues:u,isLoading:l,write:n,tap:o,press:r,release:i}=useContext(e),c=(e=>s.find(s=>s.tagName===e))(a);if(!c)throw new Error(`useAutoCoreTag: unknown tagName '${String(a)}'`);const m=useCallback(e=>n(a,e),[n,a]),C=useCallback(()=>o?.(a),[o,a]),g=useCallback(()=>r?.(a),[r,a]),b=useCallback(()=>i?.(a),[i,a]);return{value:t[a],rawValue:u[a],write:m,tap:C,press:g,release:b,isLoading:l}},useAutoCoreTags:s=>{const{values:a,rawValues:t,isLoading:u,write:l}=useContext(e);return{values:useMemo(()=>Object.fromEntries(s.map(e=>[e,a[e]])),[a,s]),rawValues:useMemo(()=>Object.fromEntries(s.map(e=>[e,t[e]])),[t,s]),write:useCallback(async(e,s)=>{await l(e,s)},[l]),isLoading:u}},useAutoCoreSelect:(s,a=[])=>{const{values:t,isLoading:u}=useContext(e);return{selected:useMemo(()=>s(t),[t,...a]),isLoading:u}},useScales:()=>{const{scales:s,updateScale:a}=useContext(e),t=useCallback(e=>s[e]??{name:e,scale:1,label:""},[s]);return{scales:s,updateScale:a,getScale:t}},useUnits:()=>{const{units:s,activeSystem:a,setUnitSystem:t,formatQuantity:u,scales:l}=useContext(e),n=useCallback(e=>l[e]?.label??"",[l]),o=useCallback((e,s)=>u?u(e,s):String(s),[u]);return{units:s??null,activeSystem:a??null,systems:s?.systems??[],setUnitSystem:t,label:n,format:o}}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcops/autocore-react",
3
- "version": "3.3.124",
3
+ "version": "3.5.3",
4
4
  "description": "A React component library for industrial user interfaces.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -0,0 +1,88 @@
1
+ /*
2
+ * Copyright (C) 2026 Automated Design Corp. All Rights Reserved.
3
+ *
4
+ * UnitSystemSelector — the operator's Metric/Imperial (or any named system)
5
+ * switch.
6
+ *
7
+ * Selecting a system writes it server-side, so it is machine-wide and survives
8
+ * a restart, and so the server's CSV export follows it rather than disagreeing
9
+ * with the screen. The server broadcasts the change and every connected client
10
+ * re-derives its scales, which means the switch is **live**: readouts, tables,
11
+ * charts and the sequence view all update with no page reload and no
12
+ * control-program restart. Nothing stored is converted — only what is rendered.
13
+ *
14
+ * See autocore-server/UNITS_PLAN.md §6.3.
15
+ */
16
+
17
+ import React, { useContext } from "react";
18
+ import { SelectButton } from "primereact/selectbutton";
19
+ import { Dropdown } from "primereact/dropdown";
20
+ import { AutoCoreTagContext } from "../core/AutoCoreTagContext";
21
+
22
+ export interface UnitSystemSelectorProps {
23
+ /**
24
+ * `"buttons"` (default) renders a segmented control — right for a
25
+ * touchscreen with two or three systems. `"dropdown"` is better past that.
26
+ */
27
+ variant?: "buttons" | "dropdown";
28
+ /** Optional label rendered above the control. */
29
+ label?: string;
30
+ /** Disable interaction (e.g. while a test is running). */
31
+ disabled?: boolean;
32
+ className?: string;
33
+ style?: React.CSSProperties;
34
+ }
35
+
36
+ export const UnitSystemSelector: React.FC<UnitSystemSelectorProps> = ({
37
+ variant = "buttons",
38
+ label,
39
+ disabled = false,
40
+ className,
41
+ style,
42
+ }) => {
43
+ const ctx = useContext(AutoCoreTagContext);
44
+ const units = ctx.units;
45
+ const active = ctx.activeSystem ?? units?.default_system ?? null;
46
+
47
+ // A machine with no units.json renders nothing rather than an empty control
48
+ // the operator can't use — the HMI still works, in backend units.
49
+ if (!units || !Array.isArray(units.systems) || units.systems.length === 0) {
50
+ return null;
51
+ }
52
+
53
+ const options = units.systems.map((s) => ({ label: s, value: s }));
54
+
55
+ const onChange = (next: string | null | undefined) => {
56
+ if (!next || next === active) return;
57
+ void ctx.setUnitSystem?.(next);
58
+ };
59
+
60
+ return (
61
+ <div className={className} style={style}>
62
+ {label ? (
63
+ <div style={{ marginBottom: "0.35rem", fontWeight: 600 }}>{label}</div>
64
+ ) : null}
65
+ {variant === "dropdown" ? (
66
+ <Dropdown
67
+ value={active}
68
+ options={options}
69
+ disabled={disabled}
70
+ onChange={(e) => onChange(e.value)}
71
+ style={{ minWidth: "10rem" }}
72
+ />
73
+ ) : (
74
+ <SelectButton
75
+ value={active}
76
+ options={options}
77
+ disabled={disabled}
78
+ // PrimeReact clears the value when the active option is
79
+ // re-clicked; ignore that so there is always a system.
80
+ onChange={(e) => onChange(e.value)}
81
+ allowEmpty={false}
82
+ />
83
+ )}
84
+ </div>
85
+ );
86
+ };
87
+
88
+ export default UnitSystemSelector;