@adcops/autocore-react 3.0.8 → 3.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ToggleGroup.d.ts +3 -85
- package/dist/components/ToggleGroup.js +1 -1
- package/dist/components/ValueInput.d.ts +5 -2
- package/dist/components/ValueInput.js +1 -1
- package/dist/core/EventEmitterContext.d.ts +1 -1
- package/dist/core/EventEmitterContext.js +1 -1
- package/dist/hub/HubBase.d.ts +3 -2
- package/dist/hub/HubBase.js +1 -1
- package/dist/hub/HubSimulate.d.ts +1 -1
- package/dist/hub/HubSimulate.js +1 -1
- package/dist/hub/HubTauri.d.ts +1 -1
- package/dist/hub/HubTauri.js +1 -1
- package/dist/hub/HubWebSocket.d.ts +37 -0
- package/dist/hub/HubWebSocket.js +1 -0
- package/dist/hub/index.d.ts +2 -2
- package/dist/hub/index.js +1 -1
- package/package.json +1 -1
- package/src/components/ToggleGroup.tsx +204 -348
- package/src/components/ValueInput.tsx +125 -84
- package/src/core/EventEmitterContext.tsx +4 -3
- package/src/hub/HubBase.ts +5 -4
- package/src/hub/HubSimulate.ts +3 -2
- package/src/hub/HubTauri.ts +7 -3
- package/src/hub/HubWebSocket.ts +153 -0
- package/src/hub/index.ts +20 -6
- package/dist/hub/HubSocketIo.d.ts +0 -101
- package/dist/hub/HubSocketIo.js +0 -1
- package/src/hub/HubSocketIo.ts +0 -166
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
* Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
|
|
3
3
|
* Created Date: 2024-03-20 13:05:42
|
|
4
4
|
* -----
|
|
5
|
-
* Last Modified: 2024-
|
|
5
|
+
* Last Modified: 2024-04-16 19:06:17
|
|
6
6
|
* -----
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
import React from "react";
|
|
11
|
+
import React, { createRef } from "react";
|
|
12
12
|
|
|
13
|
-
import { InputNumber} from 'primereact/inputnumber';
|
|
13
|
+
import { InputNumber } from 'primereact/inputnumber';
|
|
14
14
|
import { Button } from "primereact/button";
|
|
15
15
|
|
|
16
|
-
import { EventEmitterContext } from "../core/EventEmitterContext
|
|
16
|
+
import { EventEmitterContext } from "../core/EventEmitterContext";
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Properties of the ValueInput component.
|
|
@@ -23,23 +23,23 @@ interface ValueInputProps {
|
|
|
23
23
|
/**
|
|
24
24
|
* The label for the ValueInput field.
|
|
25
25
|
*/
|
|
26
|
-
label
|
|
26
|
+
label: React.ReactNode | undefined;
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* The value for the field.
|
|
31
31
|
*/
|
|
32
|
-
value
|
|
32
|
+
value: number | null;
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Minimum value for the field.
|
|
36
36
|
*/
|
|
37
|
-
min
|
|
37
|
+
min: number | undefined;
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Minimum value for the field.
|
|
41
41
|
*/
|
|
42
|
-
max
|
|
42
|
+
max: number | undefined;
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Minimum number of decimal points. The user will not
|
|
@@ -49,7 +49,7 @@ interface ValueInputProps {
|
|
|
49
49
|
* the component will throw an error.
|
|
50
50
|
* @default 0
|
|
51
51
|
*/
|
|
52
|
-
minPrecision
|
|
52
|
+
minPrecision: number | undefined;
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
/**
|
|
@@ -58,7 +58,7 @@ interface ValueInputProps {
|
|
|
58
58
|
* the component will throw an error.
|
|
59
59
|
* @default 3
|
|
60
60
|
*/
|
|
61
|
-
maxPrecision
|
|
61
|
+
maxPrecision: number | undefined;
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -67,7 +67,7 @@ interface ValueInputProps {
|
|
|
67
67
|
* currency type using the currency property.
|
|
68
68
|
* @default "decimal"
|
|
69
69
|
*/
|
|
70
|
-
mode
|
|
70
|
+
mode: "currency" | "decimal" | undefined;
|
|
71
71
|
|
|
72
72
|
|
|
73
73
|
/**
|
|
@@ -78,19 +78,19 @@ interface ValueInputProps {
|
|
|
78
78
|
*
|
|
79
79
|
* @default "USD"
|
|
80
80
|
*/
|
|
81
|
-
currency
|
|
82
|
-
|
|
81
|
+
currency: string;
|
|
82
|
+
|
|
83
83
|
/**
|
|
84
84
|
* An optional prefix before the value of the field.
|
|
85
85
|
* Unlike the TextInput control, this prefix is internal to the field.
|
|
86
86
|
*/
|
|
87
|
-
prefix
|
|
87
|
+
prefix: string | undefined;
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* An optional suffix after the value of the field.
|
|
91
91
|
* Unlike the TextInput control, this prefix is internal to the field.
|
|
92
92
|
*/
|
|
93
|
-
suffix
|
|
93
|
+
suffix: string | undefined;
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
96
|
* Set true to display buttons to increment/decrement the value.
|
|
@@ -98,12 +98,12 @@ interface ValueInputProps {
|
|
|
98
98
|
*
|
|
99
99
|
* @default false
|
|
100
100
|
*/
|
|
101
|
-
showButtons
|
|
101
|
+
showButtons: boolean;
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
104
|
* The amount clicking an increment/decrement buttion will change the value.
|
|
105
105
|
*/
|
|
106
|
-
step
|
|
106
|
+
step: number | undefined;
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
/**
|
|
@@ -112,40 +112,41 @@ interface ValueInputProps {
|
|
|
112
112
|
*
|
|
113
113
|
* @default "en-US"
|
|
114
114
|
*/
|
|
115
|
-
locale
|
|
115
|
+
locale: string | undefined;
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
118
|
* A small, advisory text below the field.
|
|
119
119
|
*/
|
|
120
|
-
description
|
|
120
|
+
description: React.ReactNode | undefined;
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* If true, all functions of the field will be disabled.
|
|
124
124
|
*/
|
|
125
|
-
disabled
|
|
126
|
-
|
|
125
|
+
disabled: boolean | undefined;
|
|
126
|
+
|
|
127
127
|
/** Topic on which the value will be dispatched through the user interfave on successful data entry. */
|
|
128
|
-
dispatchTopic
|
|
128
|
+
dispatchTopic: string | undefined;
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
131
|
* Placeholder string to display if the value is empty.
|
|
132
132
|
*/
|
|
133
|
-
placeholder
|
|
133
|
+
placeholder: string | undefined;
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
136
|
* The user has accepted a new value.
|
|
137
137
|
* @param newValue New value accepted by the user.
|
|
138
|
-
*/
|
|
139
|
-
onValueChanged?(newValue: number)
|
|
138
|
+
*/
|
|
139
|
+
onValueChanged?(newValue: number): void;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
143
|
* State variables of the ValueInput component.
|
|
144
144
|
*/
|
|
145
145
|
interface ValueInputState {
|
|
146
|
-
|
|
147
|
-
entryValue
|
|
148
|
-
|
|
146
|
+
|
|
147
|
+
entryValue: number | null;
|
|
148
|
+
currentValue: number | null;
|
|
149
|
+
editing: boolean;
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
|
|
@@ -165,67 +166,97 @@ export class ValueInput extends React.Component<ValueInputProps, ValueInputState
|
|
|
165
166
|
declare context: React.ContextType<typeof EventEmitterContext>;
|
|
166
167
|
|
|
167
168
|
|
|
168
|
-
|
|
169
|
+
|
|
169
170
|
|
|
170
171
|
/**
|
|
171
172
|
* Default properties for the component.
|
|
172
173
|
*/
|
|
173
174
|
static defaultProps = {
|
|
174
|
-
label
|
|
175
|
-
value
|
|
176
|
-
keyFilter
|
|
177
|
-
writeTopic
|
|
178
|
-
onValueChanged
|
|
175
|
+
label: '',
|
|
176
|
+
value: undefined,
|
|
177
|
+
keyFilter: undefined,
|
|
178
|
+
writeTopic: undefined,
|
|
179
|
+
onValueChanged: undefined,
|
|
179
180
|
description: undefined,
|
|
180
181
|
prefix: undefined,
|
|
181
|
-
suffix
|
|
182
|
-
disabled
|
|
183
|
-
dispatchTopic
|
|
184
|
-
placeholder
|
|
185
|
-
validator
|
|
186
|
-
min
|
|
187
|
-
max
|
|
188
|
-
minPrecision
|
|
189
|
-
maxPrecision
|
|
190
|
-
mode
|
|
191
|
-
showButtons
|
|
192
|
-
step
|
|
193
|
-
locale
|
|
194
|
-
currency
|
|
182
|
+
suffix: undefined,
|
|
183
|
+
disabled: false,
|
|
184
|
+
dispatchTopic: undefined,
|
|
185
|
+
placeholder: undefined,
|
|
186
|
+
validator: undefined,
|
|
187
|
+
min: undefined,
|
|
188
|
+
max: undefined,
|
|
189
|
+
minPrecision: 0,
|
|
190
|
+
maxPrecision: 3,
|
|
191
|
+
mode: "decimal",
|
|
192
|
+
showButtons: false,
|
|
193
|
+
step: 1,
|
|
194
|
+
locale: "en-US",
|
|
195
|
+
currency: "USD"
|
|
195
196
|
};
|
|
197
|
+
inputRef: React.RefObject<HTMLInputElement>;
|
|
196
198
|
|
|
197
199
|
/**
|
|
198
200
|
*
|
|
199
201
|
* @param {FooterViewProps} props
|
|
200
202
|
*/
|
|
201
|
-
constructor(props
|
|
203
|
+
constructor(props: ValueInputProps) {
|
|
202
204
|
super(props);
|
|
203
205
|
this.state = {
|
|
204
|
-
entryValue
|
|
205
|
-
|
|
206
|
+
entryValue: props.value,
|
|
207
|
+
currentValue: props.value,
|
|
208
|
+
editing: false
|
|
206
209
|
};
|
|
210
|
+
|
|
211
|
+
this.inputRef = createRef();
|
|
207
212
|
}
|
|
208
213
|
|
|
209
214
|
/**
|
|
210
215
|
* The component has been loaded into the DOM.
|
|
211
216
|
*/
|
|
212
|
-
componentDidMount() {
|
|
213
|
-
}
|
|
217
|
+
componentDidMount() {
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
private onBufferValue(val: number | null) {
|
|
222
|
+
if (val === null)
|
|
223
|
+
return;
|
|
214
224
|
|
|
215
|
-
|
|
225
|
+
if (!this.state.editing) {
|
|
226
|
+
this.setState({
|
|
227
|
+
entryValue: this.state.currentValue,
|
|
228
|
+
editing: true
|
|
229
|
+
}, () => {
|
|
230
|
+
|
|
231
|
+
setTimeout(() => {
|
|
232
|
+
if (this.inputRef.current) {
|
|
233
|
+
this.inputRef.current.focus();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
}, 0);
|
|
237
|
+
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
this.setState({
|
|
243
|
+
entryValue: val
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
216
247
|
|
|
217
248
|
/**
|
|
218
249
|
* The user has elected to accept the input value. Validate and store, if valid.
|
|
219
250
|
*/
|
|
220
251
|
private onAcceptValue() {
|
|
221
|
-
if (this.state.entryValue !== null
|
|
222
|
-
this.setState({editing :
|
|
223
|
-
|
|
252
|
+
if (this.state.entryValue !== null) {
|
|
253
|
+
this.setState({ editing: false, currentValue: this.state.entryValue });
|
|
254
|
+
|
|
224
255
|
if (this.props.onValueChanged)
|
|
225
256
|
this.props.onValueChanged(this.state.entryValue);
|
|
226
257
|
|
|
227
258
|
if (this.props.dispatchTopic !== undefined) {
|
|
228
|
-
this.context.dispatch({topic: this.props.dispatchTopic, payload:this.state.entryValue});
|
|
259
|
+
this.context.dispatch({ topic: this.props.dispatchTopic, payload: this.state.entryValue });
|
|
229
260
|
}
|
|
230
261
|
|
|
231
262
|
}
|
|
@@ -235,22 +266,29 @@ export class ValueInput extends React.Component<ValueInputProps, ValueInputState
|
|
|
235
266
|
* The user wishes to reset/cancel the previous value.
|
|
236
267
|
*/
|
|
237
268
|
private onResetValue() {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
269
|
+
|
|
270
|
+
if (this.state.editing) {
|
|
271
|
+
|
|
272
|
+
this.setState({
|
|
273
|
+
currentValue: this.state.currentValue,
|
|
274
|
+
entryValue: this.state.currentValue,
|
|
275
|
+
editing: false
|
|
276
|
+
});
|
|
277
|
+
}
|
|
242
278
|
}
|
|
243
279
|
|
|
244
280
|
|
|
245
281
|
render() {
|
|
246
282
|
|
|
247
|
-
return(
|
|
283
|
+
return (
|
|
248
284
|
<div>
|
|
249
285
|
<div className="p-inputgroup flex-1" >
|
|
250
286
|
<span className="p-inputgroup-addon">
|
|
251
287
|
{this.props.label}
|
|
252
|
-
</span>
|
|
253
|
-
<InputNumber
|
|
288
|
+
</span>
|
|
289
|
+
<InputNumber
|
|
290
|
+
inputRef={this.inputRef}
|
|
291
|
+
key={this.state.editing ? "editing" : "not-editing"}
|
|
254
292
|
min={this.props.min}
|
|
255
293
|
max={this.props.max}
|
|
256
294
|
minFractionDigits={this.props.minPrecision}
|
|
@@ -260,14 +298,14 @@ export class ValueInput extends React.Component<ValueInputProps, ValueInputState
|
|
|
260
298
|
suffix={this.props.suffix}
|
|
261
299
|
showButtons={this.props.showButtons}
|
|
262
300
|
step={this.props.step}
|
|
263
|
-
placeholder={this.props.placeholder}
|
|
264
|
-
value={this.state.
|
|
265
|
-
onChange={(e) => {this.
|
|
301
|
+
placeholder={this.props.placeholder}
|
|
302
|
+
value={this.state.currentValue}
|
|
303
|
+
onChange={(e) => { this.onBufferValue(e.value) }}
|
|
266
304
|
|
|
267
305
|
buttonLayout="horizontal"
|
|
268
|
-
decrementButtonClassName="p-button-danger"
|
|
269
|
-
incrementButtonClassName="p-button-success"
|
|
270
|
-
incrementButtonIcon="pi pi-plus"
|
|
306
|
+
decrementButtonClassName="p-button-danger"
|
|
307
|
+
incrementButtonClassName="p-button-success"
|
|
308
|
+
incrementButtonIcon="pi pi-plus"
|
|
271
309
|
decrementButtonIcon="pi pi-minus"
|
|
272
310
|
|
|
273
311
|
locale="en-US"
|
|
@@ -280,32 +318,35 @@ export class ValueInput extends React.Component<ValueInputProps, ValueInputState
|
|
|
280
318
|
else if (e.key === 'Escape') {
|
|
281
319
|
this.onResetValue();
|
|
282
320
|
}
|
|
283
|
-
}}
|
|
284
|
-
disabled={this.props.disabled}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
321
|
+
}}
|
|
322
|
+
disabled={this.props.disabled}
|
|
323
|
+
autoFocus={false}
|
|
324
|
+
/>
|
|
325
|
+
<Button
|
|
326
|
+
icon="pi pi-check"
|
|
288
327
|
disabled={this.props.disabled || !this.state.editing}
|
|
289
|
-
className="p-button-success"
|
|
290
|
-
onClick={() => this.onAcceptValue()}
|
|
328
|
+
className="p-button-success"
|
|
329
|
+
onClick={() => this.onAcceptValue()}
|
|
291
330
|
visible={this.state.editing}
|
|
292
331
|
size="small"
|
|
332
|
+
autoFocus={false}
|
|
293
333
|
/>
|
|
294
334
|
|
|
295
|
-
<Button
|
|
296
|
-
icon="pi pi-times"
|
|
335
|
+
<Button
|
|
336
|
+
icon="pi pi-times"
|
|
297
337
|
disabled={this.props.disabled || !this.state.editing}
|
|
298
|
-
className="p-button-danger"
|
|
299
|
-
onClickCapture={()=>this.onResetValue()}
|
|
338
|
+
className="p-button-danger"
|
|
339
|
+
onClickCapture={() => this.onResetValue()}
|
|
300
340
|
visible={this.state.editing}
|
|
301
341
|
size="small"
|
|
342
|
+
autoFocus={false}
|
|
302
343
|
/>
|
|
303
344
|
</div>
|
|
304
|
-
|
|
345
|
+
|
|
305
346
|
{this.props.description !== undefined &&
|
|
306
347
|
<small>{this.props.description}</small>
|
|
307
348
|
}
|
|
308
|
-
|
|
349
|
+
|
|
309
350
|
|
|
310
351
|
</div>
|
|
311
352
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (C) 2024 Automated Design Corp. All Rights Reserved.
|
|
3
3
|
* Created Date: 2024-01-17 11:45:10
|
|
4
4
|
* -----
|
|
5
|
-
* Last Modified: 2024-
|
|
5
|
+
* Last Modified: 2024-04-23 11:53:43
|
|
6
6
|
* Modified By: ADC
|
|
7
7
|
* -----
|
|
8
8
|
*
|
|
@@ -103,7 +103,7 @@ export interface EventEmitterContextType {
|
|
|
103
103
|
* Invoke/send a message to the back end.
|
|
104
104
|
* This does NOT get published to the front end.
|
|
105
105
|
*/
|
|
106
|
-
invoke(fname: string, payload? : object) : Promise<object>;
|
|
106
|
+
invoke( domain: string, fname: string, payload? : object) : Promise<object>;
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
109
|
* Subscribe to events identified by the topic.
|
|
@@ -264,7 +264,8 @@ export const EventEmitterContext = createContext<EventEmitterContextType>({
|
|
|
264
264
|
state: { subscriptions: {}, nextSubscriptionId : 1 },
|
|
265
265
|
dispatch: () => { },
|
|
266
266
|
subscribe: () => { return 0; }, // Placeholder for subscription logic
|
|
267
|
-
invoke: async (fname: string, payload?: object) => {
|
|
267
|
+
invoke: async (domain: string, fname: string, payload?: object) => {
|
|
268
|
+
domain;
|
|
268
269
|
fname;
|
|
269
270
|
payload;
|
|
270
271
|
// Placeholder for invoke logic
|
package/src/hub/HubBase.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Created Date: 2023-12-15 14:21:33
|
|
4
4
|
* Author: Thomas C. Bitsky Jr.
|
|
5
5
|
* -----
|
|
6
|
-
* Last Modified: 2024-
|
|
6
|
+
* Last Modified: 2024-04-23 11:19:03
|
|
7
7
|
* Modified By: ADC
|
|
8
8
|
* -----
|
|
9
9
|
*
|
|
@@ -122,23 +122,24 @@ export abstract class HubBase {
|
|
|
122
122
|
* Invoke/send a message to the back end.
|
|
123
123
|
* This does NOT get published to the front end.
|
|
124
124
|
*/
|
|
125
|
-
abstract invoke(fname: string, payload? : object) : Promise<object>;
|
|
125
|
+
abstract invoke(domain: string, fname: string, payload? : object) : Promise<object>;
|
|
126
126
|
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
129
|
* Convenience function to invoke a command with an optional topic and optional value.
|
|
130
130
|
* This will invoke the specified command in the backend. It does not broadcast the
|
|
131
131
|
* value within the local frontend.
|
|
132
|
+
* @param domain string The domain of the command that will be invoked.
|
|
132
133
|
* @param fname string Function name
|
|
133
134
|
* @param topic string Topic
|
|
134
135
|
* @param value any data payload
|
|
135
136
|
*/
|
|
136
|
-
invokeTopic( fname : string, topic : string | null, value? : any ) : Promise<object> {
|
|
137
|
+
invokeTopic( domain: string, fname : string, topic : string | null, value? : any ) : Promise<object> {
|
|
137
138
|
const msg = {
|
|
138
139
|
topic : topic,
|
|
139
140
|
data : value
|
|
140
141
|
};
|
|
141
|
-
return this.invoke(fname, msg);
|
|
142
|
+
return this.invoke(domain, fname, msg);
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
/**
|
package/src/hub/HubSimulate.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (C) 2023 Automated Design Corp. All Rights Reserved.
|
|
3
3
|
* Created Date: 2023-12-17 10:38:21
|
|
4
4
|
* -----
|
|
5
|
-
* Last Modified: 2024-
|
|
5
|
+
* Last Modified: 2024-04-23 11:23:27
|
|
6
6
|
* Modified By: ADC
|
|
7
7
|
* -----
|
|
8
8
|
*
|
|
@@ -31,7 +31,8 @@ export class HubSimulate extends HubBase {
|
|
|
31
31
|
* @param timeout Timeout in milliseconds after which the promise is rejected if no response is received.
|
|
32
32
|
* @returns A Promise that resolves to the response from the backend or rejects if a timeout occurs.
|
|
33
33
|
*/
|
|
34
|
-
invoke(fname: string, payload?: object, timeout: number = 20): Promise<object> {
|
|
34
|
+
invoke(domain : string, fname: string, payload?: object, timeout: number = 20): Promise<object> {
|
|
35
|
+
domain; // Not using for simulator
|
|
35
36
|
fname; // Not using for simulator
|
|
36
37
|
return new Promise((resolve, reject) => {
|
|
37
38
|
reject; // not using for simulator
|
package/src/hub/HubTauri.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Created Date: 2023-12-17 09:50:23
|
|
4
4
|
* Author: Thomas C. Bitsky Jr.
|
|
5
5
|
* -----
|
|
6
|
-
* Last Modified: 2024-
|
|
6
|
+
* Last Modified: 2024-04-23 11:21:45
|
|
7
7
|
* Modified By: ADC
|
|
8
8
|
* -----
|
|
9
9
|
*
|
|
@@ -128,13 +128,17 @@ export class HubTauri extends HubBase {
|
|
|
128
128
|
* @param payload Optional data payload
|
|
129
129
|
* @returns The return value of the backend method.
|
|
130
130
|
*/
|
|
131
|
-
invoke(fname: string, payload?: object | undefined): Promise<object> {
|
|
131
|
+
invoke(domain : string, fname: string, payload?: object | undefined): Promise<object> {
|
|
132
132
|
|
|
133
133
|
console.log(JSON.stringify(event));
|
|
134
134
|
|
|
135
135
|
if (payload !== undefined && payload !== null) {
|
|
136
136
|
console.log(`Payload: ${JSON.stringify(payload)}`);
|
|
137
|
-
|
|
137
|
+
|
|
138
|
+
// todo! This should probably always been the same function, and then that function
|
|
139
|
+
// in the backend routes the command.
|
|
140
|
+
let topic = `${domain}_${fname}`;
|
|
141
|
+
return invoke(topic, payload as InvokeArgs);
|
|
138
142
|
}
|
|
139
143
|
else {
|
|
140
144
|
return invoke(fname);
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Automated Design Corp.. All Rights Reserved.
|
|
3
|
+
* Created Date: 2024-04-17 09:13:07
|
|
4
|
+
* -----
|
|
5
|
+
* Last Modified: 2024-04-23 18:48:23
|
|
6
|
+
* -----
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** @file
|
|
11
|
+
*
|
|
12
|
+
* ## broadcast messages
|
|
13
|
+
* The result field of CommandMessages used for broadcast must have
|
|
14
|
+
* success set to true, and must contain the field "topic." The topic field, along
|
|
15
|
+
* with the domain field of the CommandMessage will be combined for the topic used
|
|
16
|
+
* by the websocket client.
|
|
17
|
+
*
|
|
18
|
+
* broadcast topic = `${CommandMessage.domain}/${CommandMessage.result["topic"]}
|
|
19
|
+
*
|
|
20
|
+
* So, to subscribe to a broadcast topic for GM.fReal that is served by the ADS client, the
|
|
21
|
+
* topic will be: "ADS/GM.fReal"
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { HubBase } from './HubBase';
|
|
25
|
+
|
|
26
|
+
/// The result portion of a CommandMessage. The server will
|
|
27
|
+
/// place the result of a command in this portion of the message.
|
|
28
|
+
interface CommandMessageResult {
|
|
29
|
+
/// The JSON object of the result.
|
|
30
|
+
data: any;
|
|
31
|
+
/// If true, the command was processed successfully.
|
|
32
|
+
success : boolean,
|
|
33
|
+
/// If success is false, this should contain a corresponding error message.
|
|
34
|
+
error_message: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// CommandMessage is the object passed between the client and server for making requests and
|
|
38
|
+
/// responses.
|
|
39
|
+
interface CommandMessage {
|
|
40
|
+
/// An id to identify the request. The request ID is managed by the client and will be reflected back.
|
|
41
|
+
/// The server will never change the request ID.
|
|
42
|
+
request_id: number;
|
|
43
|
+
/// The command domain/servelet in which this command/function to invoke belongs.
|
|
44
|
+
domain: string;
|
|
45
|
+
/// The name of the function to invoke.
|
|
46
|
+
fname: string;
|
|
47
|
+
/// A JSON object of the arguments to the function.
|
|
48
|
+
args?: any;
|
|
49
|
+
/// The result of the command, reflected back from the server
|
|
50
|
+
result?: CommandMessageResult;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Function to parse the JSON string received from the server into a CommandMessage object
|
|
54
|
+
// function parseCommandMessage(jsonString: string): CommandMessage {
|
|
55
|
+
// const parsed: CommandMessage = JSON.parse(jsonString);
|
|
56
|
+
// // Assuming the JSON structure directly matches the TypeScript interfaces
|
|
57
|
+
// return parsed;
|
|
58
|
+
// }
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
interface RequestRecord {
|
|
62
|
+
resolve: (value?: any) => void;
|
|
63
|
+
reject: (reason?: any) => void;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
export class HubWebSocket extends HubBase {
|
|
68
|
+
private socket: WebSocket;
|
|
69
|
+
private requestId = 0;
|
|
70
|
+
private pendingRequests = new Map<number, RequestRecord>();
|
|
71
|
+
|
|
72
|
+
constructor() {
|
|
73
|
+
super();
|
|
74
|
+
|
|
75
|
+
const host = window.location.hostname; // Get the hostname from the address bar
|
|
76
|
+
const port = window.location.port; // Get the port from the address bar, if any
|
|
77
|
+
const proto = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; // Determine the protocol
|
|
78
|
+
const wsUrl = `${proto}${host}${port ? ':' + port : ''}/ws/`; // Construct WebSocket URL
|
|
79
|
+
|
|
80
|
+
this.socket = new WebSocket(wsUrl);
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
let self = this;
|
|
84
|
+
this.socket.onopen = function() {
|
|
85
|
+
console.log("WebSocket connection established.");
|
|
86
|
+
self.publish("HUB/connected", true);
|
|
87
|
+
//ws.send("Hello, server!"); // Send a message to the server
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
this.socket.onmessage = (event) => {
|
|
92
|
+
const data: CommandMessage = JSON.parse(event.data);
|
|
93
|
+
if (data.request_id && this.pendingRequests.has(data.request_id)) {
|
|
94
|
+
const { resolve, reject } = this.pendingRequests.get(data.request_id)!;
|
|
95
|
+
if (!data.result?.success) {
|
|
96
|
+
reject(new Error(data.result?.error_message));
|
|
97
|
+
} else {
|
|
98
|
+
resolve(data);
|
|
99
|
+
}
|
|
100
|
+
this.pendingRequests.delete(data.request_id);
|
|
101
|
+
} else {
|
|
102
|
+
this.handleUnsolicitedMessage(data);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
this.socket.onerror = (error: Event) => {
|
|
107
|
+
console.error('WebSocket error:', error);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
this.socket.onclose = () => {
|
|
111
|
+
console.log('WebSocket connection closed.');
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
invoke = (domain : string, fname: string, payload?: object): Promise<object> => {
|
|
116
|
+
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
const id = ++this.requestId; // Increment and use the request ID
|
|
119
|
+
this.pendingRequests.set(id, { resolve, reject });
|
|
120
|
+
|
|
121
|
+
let cm : CommandMessage = {
|
|
122
|
+
request_id: id,
|
|
123
|
+
domain: domain,
|
|
124
|
+
fname: fname,
|
|
125
|
+
args: payload,
|
|
126
|
+
result: undefined
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
this.socket.send(JSON.stringify(cm));
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
handleUnsolicitedMessage = (msg: CommandMessage) => {
|
|
134
|
+
// Handle messages that do not correspond to a pending request
|
|
135
|
+
|
|
136
|
+
if (msg.fname === "BROADCAST"
|
|
137
|
+
&& msg.domain.length >= 1
|
|
138
|
+
&& msg.result !== undefined && msg.result !== null
|
|
139
|
+
&& msg.result.data !== undefined && msg.result.data !== null
|
|
140
|
+
) {
|
|
141
|
+
let topic = `${msg.domain}/${msg.result.data["topic"]}`;
|
|
142
|
+
this.publish(topic, msg.result.data);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
disconnect= (): void => {
|
|
147
|
+
this.socket.close();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
protected emit = (eventName: string, payload?: object): void => {
|
|
151
|
+
this.socket.send(JSON.stringify({ eventName, payload }));
|
|
152
|
+
}
|
|
153
|
+
}
|