Skip to content

Commit 36cdd22

Browse files
authored
Merge branch 'master' (#2108)
1 parent 39e51cf commit 36cdd22

29 files changed

+2002
-71
lines changed

client/dist/assets/i18n/en.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,9 +967,11 @@
967967
"device.list-max": "Max",
968968
"device.list-value": "Value",
969969
"device.list-timestamp": "Timestamp",
970+
"device.list-quality": "Quality",
970971
"device.list-description": "Description",
971972
"device.list-edit": "Edit Tag",
972973
"device.list-add": "Add Tag",
974+
"device.list-scan": "Scan Tags",
973975
"device.list-remove": "Remove Tag",
974976
"device.list-remove-all": "Remove all Tags",
975977
"device.list-options": "Tag options",
@@ -1058,6 +1060,31 @@
10581060
"device.property-melsec-target": "Address (IP[:port])",
10591061
"device.property-melsec-ascii": "ASCII",
10601062
"device.property-melsec-octalIO": "Octal I/O",
1063+
"device.property-redis-target": "Connection URL",
1064+
"device.property-redis-readmode-simple": "Simple",
1065+
"device.property-redis-readmode-hash": "Hash",
1066+
"device.property-redis-readmode-custom": "Custom",
1067+
"device.property-redis-readMode": "Read Mode",
1068+
"device.property-redis-readName": "Read Command Name",
1069+
"device.property-redis-read-keyValue": "Read Value-Key",
1070+
"device.property-redis-read-keyValue-ph": "(ex. Value)",
1071+
"device.property-redis-read-keyTimestamp": "Read Timestamp-Key",
1072+
"device.property-redis-read-keyTimestamp-ph": "(ex. UtcTimeMs)",
1073+
"device.property-redis-read-keyQuality": "Read Quality-Key",
1074+
"device.property-redis-read-keyQuality-ph": "(ex. Quality)",
1075+
"device.property-redis-write-addkey": "Add Write-Key",
1076+
"device.property-redis-writeName": "Write Command Name",
1077+
"device.property-redis-writeName-ph": "(ex. DAINSY.HSET)",
1078+
"device.property-redis-write-keyName": "Write Key-Name",
1079+
"device.property-redis-write-keyValue": "Write Key-Value",
1080+
"device.property-redis-write-args-tooltip": "Under Write Key-Name / Write Key-Value: use {{token}} for dynamic values such as {{timestamp}}, {{provider}}, {{quality}}.\n\nValues without {{}} are treated as literals.\n\nExample: Name=Provider, Value=fuxa — or Name=UtcTimeMs, Value={{timestamp}}.",
1081+
"device.property-redis-scan-title": "Scan Tags to import",
1082+
"device.property-redis-scan-filter-query": "Filter (pattern MATCH)",
1083+
"device.property-redis-scan-filter-list": "List filter",
1084+
"device.property-redis-scan-unselect": "Unselect all (filtered)",
1085+
"device.property-redis-scan-select": "Select all (filtered)",
1086+
"device.property-redis-scan-scanning": "Scanning…",
1087+
"device.property-redis-scan-found": "Found",
10611088

10621089
"device.browsetopics-property-title": "Broker Topics to subscribe and publish",
10631090
"device.browsetopics-property-sub": "Subscribe",

client/dist/main.818223a5d16eafbd.js

Lines changed: 180 additions & 1 deletion
Large diffs are not rendered by default.

client/src/app/_models/device.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class Device {
3939
id: 'Device id, GUID',
4040
name: 'Device name',
4141
enabled: 'Enabled',
42-
type: 'Device Type: FuxaServer | SiemensS7 | OPCUA | BACnet | ModbusRTU | ModbusTCP | WebAPI | MQTTclient | internal | EthernetIP | ADSclient | Gpio | WebCam | MELSEC',
42+
type: 'Device Type: FuxaServer | SiemensS7 | OPCUA | BACnet | ModbusRTU | ModbusTCP | WebAPI | MQTTclient | internal | EthernetIP | ADSclient | Gpio | WebCam | MELSEC | REDIS',
4343
polling: 'Polling interval in millisec., check changed value after ask value, by OPCUA there is a monitor',
4444
property: 'Connection property depending of type',
4545
tags: 'Tags list of Tag',
@@ -180,13 +180,13 @@ export class DeviceNetProperty {
180180
stopbits: string;
181181
/** Serial parity used for Modbus RTU connection */
182182
parity: string;
183-
/** Options settings used for Modbus tockenized frame */
183+
/** Options settings used for Modbus tockenized frame, redis for field */
184184
options: string;
185185
/** Method flag used for WebAPI (GET/POST) */
186186
method: string;
187187
/** Data format flag used for WebAPI (CSV/JSON) */
188188
format: string;
189-
/** Connection option used for Modbus RTU/TCP */
189+
/** Connection option used for Modbus RTU/TCP, Redis for readMode */
190190
connectionOption: string;
191191
/** Delay used for Modbus RTU/TCP delay between frame*/
192192
delay: number = 10;
@@ -247,7 +247,8 @@ export enum DeviceType {
247247
ADSclient = 'ADSclient',
248248
GPIO = 'GPIO',
249249
WebCam = 'WebCam',
250-
MELSEC = 'MELSEC'
250+
MELSEC = 'MELSEC',
251+
REDIS = 'REDIS'
251252
// Template: 'template'
252253
}
253254

@@ -320,6 +321,39 @@ export enum MelsecTagType {
320321
STRING = 'STRING'
321322
}
322323

324+
export enum RedisReadModeType {
325+
simple = 'simple',
326+
hash = 'hash',
327+
// custom = 'custom',
328+
}
329+
330+
export enum RedisTagType {
331+
number = 'number',
332+
boolean = 'boolean',
333+
string = 'string'
334+
}
335+
336+
export class RedisOptions {
337+
redisTimeoutMs: number = 3000;
338+
maxKeysPerPoll = 500;
339+
readFields = {
340+
value: '',
341+
quality: '',
342+
timestamp: ''
343+
};
344+
customCommand = {
345+
read: {
346+
name: '',
347+
batchPerKey: true,
348+
args: ['{{key}}','{{fields...}}']
349+
},
350+
write: {
351+
name: '',
352+
args: []
353+
}
354+
};
355+
}
356+
323357
export enum ModbusOptionType {
324358
SerialPort = 'SerialPort',
325359
RTUBufferedPort = 'RTUBufferedPort',

client/src/app/_models/hmi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ export class Variable {
611611
error: number;
612612
timestamp: number;
613613
device?: Device;
614+
quality?: string;
614615
constructor(id: string, name: string, device?: Device) {
615616
this.id = id;
616617
this.name = name;

client/src/app/_models/plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ export enum PluginType {
1616
Raspberry = 'Raspberry',
1717
SiemensS7 = 'SiemensS7',
1818
EthernetIP = 'EthernetIP',
19-
MELSEC = 'MELSEC'
19+
MELSEC = 'MELSEC',
20+
REDIS = 'REDIS'
2021
}
2122

2223
export enum PluginGroupType {

client/src/app/_services/hmi.service.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,24 +239,26 @@ export class HmiService {
239239
});
240240
// devices values
241241
this.socket.on(IoEventTypes.DEVICE_VALUES, (message) => {
242-
const updateVariable = (id: string, value: any, timestamp: any) => {
242+
const updateVariable = (id: string, value: any, timestamp: any, quality: any) => {
243243
if (Utils.isNullOrUndefined(this.variables[id])) {
244244
this.variables[id] = new Variable(id, null, null);
245245
}
246246
this.variables[id].value = value;
247247
this.variables[id].timestamp = timestamp;
248+
this.variables[id].quality = quality;
248249
this.setSignalValue(this.variables[id]);
249250
};
250251

251252
for (let idx = 0; idx < message.values.length; idx++) {
252253
const originalId = message.values[idx].id;
253254
const value = message.values[idx].value;
254255
const timestamp = message.values[idx].timestamp;
255-
updateVariable(originalId, value, timestamp);
256+
const quality = message.values[idx].quality;
257+
updateVariable(originalId, value, timestamp, quality);
256258
const adapterIds = this.deviceAdapaterService.resolveDeviceTagIdForAdapter(originalId);
257259
if (adapterIds?.length) {
258260
adapterIds.forEach(adapterId => {
259-
updateVariable(adapterId, value, timestamp);
261+
updateVariable(adapterId, value, timestamp, quality);
260262
});
261263
}
262264
}
@@ -392,7 +394,7 @@ export class HmiService {
392394
/**
393395
* Ask device browse to backend
394396
*/
395-
public askDeviceBrowse(deviceId: string, node: any) {
397+
public askDeviceBrowse(deviceId: string, node?: any) {
396398
if (this.socket) {
397399
let msg = { device: deviceId, node: node };
398400
this.socket.emit(IoEventTypes.DEVICE_BROWSE, msg);

client/src/app/app.module.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ import { MatIconRegistry } from '@angular/material/icon';
233233
import { NodeRedFlowsComponent } from './integrations/node-red/node-red-flows/node-red-flows.component';
234234
import { ApiKeysListComponent } from './apikeys/api-keys-list/api-keys-list.component';
235235
import { ApiKeyPropertyComponent } from './apikeys/api-key-property/api-key-property.component';
236+
import { TagPropertyEditRedisComponent } from './device/tag-property/tag-property-edit-redis/tag-property-edit-redis.component';
237+
import { TagPropertyRedisScanComponent } from './device/tag-property/tag-property-edit-redis/tag-property-redis-scan/tag-property-redis-scan.component';
236238

237239
export function createTranslateLoader(http: HttpClient) {
238240
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
@@ -415,7 +417,9 @@ export const myCustomTooltipDefaults: MatTooltipDefaultOptions = {
415417
SchedulerConfirmDialogComponent,
416418
NodeRedFlowsComponent,
417419
ApiKeysListComponent,
418-
ApiKeyPropertyComponent
420+
ApiKeyPropertyComponent,
421+
TagPropertyEditRedisComponent,
422+
TagPropertyRedisScanComponent,
419423
],
420424
imports: [
421425
BrowserModule,

client/src/app/device/device-list/device-list.component.html

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,20 @@
2121
<!-- Checkbox Column -->
2222
<ng-container matColumnDef="select">
2323
<mat-header-cell *matHeaderCellDef [ngClass]="'selectidthClass'">
24-
<button *ngIf="!readonly && isDeviceToEdit" mat-icon-button (click)="onAddTag()" class="remove" matTooltip="{{'device.list-add' | translate}}">
25-
<mat-icon>add</mat-icon>
26-
</button>
24+
<ng-container *ngIf="deviceSelected?.type === deviceType.REDIS; else simpleAdd">
25+
<button mat-icon-button [matMenuTriggerFor]="addMenu" class="remove" matTooltip="{{'device.list-add' | translate}}">
26+
<mat-icon>add</mat-icon>
27+
</button>
28+
<mat-menu #addMenu [overlapTrigger]="false">
29+
<button mat-menu-item (click)="onAddTag()">{{'device.list-add' | translate}}</button>
30+
<button mat-menu-item (click)="onScanRedisTag()">{{'device.list-scan' | translate}}</button>
31+
</mat-menu>
32+
</ng-container>
33+
<ng-template #simpleAdd>
34+
<button *ngIf="!readonly && isDeviceToEdit" mat-icon-button (click)="onAddTag()" class="remove" matTooltip="{{'device.list-add' | translate}}">
35+
<mat-icon>add</mat-icon>
36+
</button>
37+
</ng-template>
2738
</mat-header-cell>
2839
<mat-cell *matCellDef="let element" [ngClass]="'selectidthClass'">
2940
<button mat-icon-button (click)="onEditRow(element)" class="remove" matTooltip="{{'device.list-edit' | translate}}"
@@ -80,6 +91,11 @@
8091
<mat-header-cell *matHeaderCellDef mat-sort-header> {{'device.list-timestamp' | translate}} </mat-header-cell>
8192
<mat-cell *matCellDef="let element"> {{element.timestamp | date : 'dd-MM-yyyy HH:mm:ss' }} </mat-cell>
8293
</ng-container>
94+
<!-- Quality Column -->
95+
<ng-container matColumnDef="quality">
96+
<mat-header-cell *matHeaderCellDef mat-sort-header> {{'device.list-quality' | translate}} </mat-header-cell>
97+
<mat-cell *matCellDef="let element"> {{element.quality}} </mat-cell>
98+
</ng-container>
8399
<ng-container matColumnDef="description">
84100
<mat-header-cell *matHeaderCellDef mat-sort-header> {{'device.list-description' | translate}} </mat-header-cell>
85101
<mat-cell *matCellDef="let element" matTooltip="{{element.description}}">{{ element.description }}</mat-cell>

client/src/app/device/device-list/device-list.component.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@
122122
flex: 0 0 120px;
123123
}
124124

125+
.mat-column-quality {
126+
flex: 0 0 100px;
127+
}
128+
125129
.selectidthClass{
126130
flex: 0 0 50px;
127131
}

client/src/app/device/device-list/device-list.component.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { TagPropertyService } from '../tag-property/tag-property.service';
2626
export class DeviceListComponent implements OnInit, AfterViewInit {
2727

2828
readonly defAllColumns = ['select', 'name', 'address', 'device', 'type', 'value', 'timestamp', 'description', 'warning', 'logger', 'options', 'remove'];
29+
readonly defAllExtColumns = ['select', 'name', 'address', 'device', 'type', 'value', 'timestamp', 'quality', 'description', 'warning', 'logger', 'options', 'remove'];
2930
readonly defInternalColumns = ['select', 'name', 'device', 'type', 'value', 'timestamp', 'description', 'options', 'remove'];
3031
readonly defGpipColumns = ['select', 'name', 'device', 'address', 'direction', 'value', 'timestamp', 'description', 'logger', 'options', 'remove'];
3132
readonly defWebcamColumns = ['select', 'name', 'device', 'address', 'value', 'timestamp', 'description', 'logger', 'options', 'remove'];
@@ -122,6 +123,9 @@ export class DeviceListComponent implements OnInit, AfterViewInit {
122123
} else if (this.deviceSelected.type === DeviceType.WebCam){
123124
this.displayedColumns = this.defWebcamColumns;
124125
this.tableWidth = this.defAllRowWidth;
126+
} else if (this.deviceSelected.type === DeviceType.REDIS) {
127+
this.displayedColumns = this.defAllExtColumns;
128+
this.tableWidth = this.defAllRowWidth;
125129
} else {
126130
this.displayedColumns = this.defAllColumns;
127131
this.tableWidth = this.defAllRowWidth;
@@ -230,6 +234,14 @@ export class DeviceListComponent implements OnInit, AfterViewInit {
230234
}
231235
}
232236

237+
onScanRedisTag() {
238+
if (this.deviceSelected.type === DeviceType.REDIS) {
239+
this.tagPropertyService.scanTagsRedis(this.deviceSelected).subscribe(result => {
240+
this.bindToTable(this.deviceSelected.tags);
241+
});
242+
}
243+
}
244+
233245
getTagLabel(tag: Tag) {
234246
if (this.deviceSelected.type === DeviceType.BACnet || this.deviceSelected.type === DeviceType.WebAPI) {
235247
return tag.label || tag.name;
@@ -264,7 +276,7 @@ export class DeviceListComponent implements OnInit, AfterViewInit {
264276
if (type === DeviceType.SiemensS7 || type === DeviceType.ModbusTCP || type === DeviceType.ModbusRTU ||
265277
type === DeviceType.internal || type === DeviceType.EthernetIP || type === DeviceType.FuxaServer ||
266278
type === DeviceType.OPCUA || type === DeviceType.GPIO || type === DeviceType.ADSclient ||
267-
type === DeviceType.WebCam || type === DeviceType.MELSEC) {
279+
type === DeviceType.WebCam || type === DeviceType.MELSEC || type === DeviceType.REDIS) {
268280
return true;
269281
} else if (type === DeviceType.MQTTclient) {
270282
if (tag && tag.options && (tag.options.pubs || tag.options.subs)) {
@@ -345,6 +357,13 @@ export class DeviceListComponent implements OnInit, AfterViewInit {
345357
});
346358
return;
347359
}
360+
if (this.deviceSelected.type === DeviceType.REDIS) {
361+
this.tagPropertyService.editTagPropertyRedis(this.deviceSelected, tag, checkToAdd).subscribe(result => {
362+
this.tagsMap[tag.id] = tag;
363+
this.bindToTable(this.deviceSelected.tags);
364+
});
365+
return;
366+
}
348367
}
349368

350369
editTagOptions(tags: Tag[]) {
@@ -374,9 +393,11 @@ export class DeviceListComponent implements OnInit, AfterViewInit {
374393
let sigs = this.hmiService.getAllSignals();
375394
for (let id in sigs) {
376395
if (this.tagsMap[id]) {
377-
this.tagsMap[id].value = sigs[id].value;
378-
this.tagsMap[id].error = sigs[id].error;
379-
this.tagsMap[id].timestamp = sigs[id].timestamp;
396+
const signal = sigs[id];
397+
this.tagsMap[id].value = signal.value;
398+
this.tagsMap[id].error = signal.error;
399+
this.tagsMap[id].timestamp = signal.timestamp;
400+
this.tagsMap[id].quality = signal.quality;
380401
}
381402
}
382403
this.changeDetector.detectChanges();

0 commit comments

Comments
 (0)