Merge with master

This commit is contained in:
Andrii Shvaika 2023-05-24 18:40:57 +03:00
commit 193b37e292
8 changed files with 75 additions and 21 deletions

View File

@ -259,17 +259,16 @@ public class ThingsboardInstallService {
case "3.5.1": case "3.5.1":
log.info("Upgrading ThingsBoard from version 3.5.1 to 3.5.2 ..."); log.info("Upgrading ThingsBoard from version 3.5.1 to 3.5.2 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.5.1"); databaseEntitiesUpgradeService.upgradeDatabase("3.5.1");
//TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache
break;
default:
throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion);
}
entityDatabaseSchemaService.createOrUpdateViewsAndFunctions(); entityDatabaseSchemaService.createOrUpdateViewsAndFunctions();
entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry); entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry);
log.info("Updating system data..."); log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets(); systemDataLoaderService.updateSystemWidgets();
installScripts.loadSystemLwm2mResources(); installScripts.loadSystemLwm2mResources();
break;
//TODO update CacheCleanupService on the next version upgrade
default:
throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion);
}
} }
log.info("Upgrade finished successfully!"); log.info("Upgrade finished successfully!");

View File

@ -84,7 +84,8 @@ public class DefaultCacheCleanupService implements CacheCleanupService {
break; break;
case "3.4.4": case "3.4.4":
log.info("Clearing cache to upgrade from version 3.4.4 to 3.5.0"); log.info("Clearing cache to upgrade from version 3.4.4 to 3.5.0");
clearCacheByName("deviceProfiles"); clearAll();
break;
default: default:
//Do nothing, since cache cleanup is optional. //Do nothing, since cache cleanup is optional.
} }

View File

@ -348,7 +348,7 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
@Override @Override
public AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId id, long clearTs, JsonNode details) { public AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId id, long clearTs, JsonNode details) {
return toAlarmApiResult(alarmRepository.clearAlarm(tenantId.getId(), id.getId(), clearTs, getDetailsAsString(details))); return toAlarmApiResult(alarmRepository.clearAlarm(tenantId.getId(), id.getId(), clearTs, details != null ? getDetailsAsString(details) : null));
} }
@Override @Override

View File

@ -226,8 +226,12 @@ BEGIN
END IF; END IF;
IF NOT(existing.cleared) THEN IF NOT(existing.cleared) THEN
cleared = TRUE; cleared = TRUE;
IF a_details IS NULL THEN
UPDATE alarm a SET cleared = true, clear_ts = a_ts WHERE a.id = a_id AND a.tenant_id = t_id;
ELSE
UPDATE alarm a SET cleared = true, clear_ts = a_ts, additional_info = a_details WHERE a.id = a_id AND a.tenant_id = t_id; UPDATE alarm a SET cleared = true, clear_ts = a_ts, additional_info = a_details WHERE a.id = a_id AND a.tenant_id = t_id;
END IF; END IF;
END IF;
SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id;
RETURN json_build_object('success', true, 'cleared', cleared, 'alarm', row_to_json(result))::text; RETURN json_build_object('success', true, 'cleared', cleared, 'alarm', row_to_json(result))::text;
END END

View File

@ -202,7 +202,34 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest {
@Test @Test
public void testClearAlarmProcedure() { public void testClearAlarmProcedure() {
UUID tenantId = UUID.randomUUID(); UUID tenantId = UUID.randomUUID();
; UUID originator1Id = UUID.fromString("d4b68f41-3e96-11e7-a884-898080180d6b");
UUID alarm1Id = UUID.fromString("d4b68f43-3e96-11e7-a884-898080180d6b");
Alarm alarm = saveAlarm(alarm1Id, tenantId, originator1Id, "TEST_ALARM");
long clearTs = System.currentTimeMillis();
var details = JacksonUtil.newObjectNode().put("test", 123);
AlarmApiCallResult result = alarmDao.clearAlarm(alarm.getTenantId(), alarm.getId(), clearTs, details);
AlarmInfo afterSave = alarmDao.findAlarmInfoById(alarm.getTenantId(), alarm.getUuidId());
assertNotNull(result);
assertTrue(result.isSuccessful());
assertTrue(result.isCleared());
assertNotNull(result.getAlarm());
assertEquals(afterSave, result.getAlarm());
assertEquals(clearTs, result.getAlarm().getClearTs());
assertTrue(result.getAlarm().isCleared());
assertEquals(details, result.getAlarm().getDetails());
result = alarmDao.clearAlarm(alarm.getTenantId(), alarm.getId(), clearTs + 1, JacksonUtil.newObjectNode());
assertNotNull(result);
assertNotNull(result.getAlarm());
assertEquals(afterSave, result.getAlarm());
assertTrue(result.isSuccessful());
assertFalse(result.isCleared());
assertEquals(clearTs, result.getAlarm().getClearTs());
assertTrue(result.getAlarm().isCleared());
}
@Test
public void testClearAlarmWithoutDetailsProcedure() {
UUID tenantId = UUID.randomUUID();
UUID originator1Id = UUID.fromString("d4b68f41-3e96-11e7-a884-898080180d6b"); UUID originator1Id = UUID.fromString("d4b68f41-3e96-11e7-a884-898080180d6b");
UUID alarm1Id = UUID.fromString("d4b68f43-3e96-11e7-a884-898080180d6b"); UUID alarm1Id = UUID.fromString("d4b68f43-3e96-11e7-a884-898080180d6b");
Alarm alarm = saveAlarm(alarm1Id, tenantId, originator1Id, "TEST_ALARM"); Alarm alarm = saveAlarm(alarm1Id, tenantId, originator1Id, "TEST_ALARM");
@ -216,6 +243,7 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest {
assertEquals(afterSave, result.getAlarm()); assertEquals(afterSave, result.getAlarm());
assertEquals(clearTs, result.getAlarm().getClearTs()); assertEquals(clearTs, result.getAlarm().getClearTs());
assertTrue(result.getAlarm().isCleared()); assertTrue(result.getAlarm().isCleared());
assertEquals(alarm.getDetails(), result.getAlarm().getDetails());
result = alarmDao.clearAlarm(alarm.getTenantId(), alarm.getId(), clearTs + 1, JacksonUtil.newObjectNode()); result = alarmDao.clearAlarm(alarm.getTenantId(), alarm.getId(), clearTs + 1, JacksonUtil.newObjectNode());
assertNotNull(result); assertNotNull(result);
assertNotNull(result.getAlarm()); assertNotNull(result.getAlarm());

View File

@ -36,7 +36,13 @@ import { Observable, of } from 'rxjs';
import { Polyline } from './polyline'; import { Polyline } from './polyline';
import { Polygon } from './polygon'; import { Polygon } from './polygon';
import { Circle } from './circle'; import { Circle } from './circle';
import { createTooltip, entitiesParseName, isCutPolygon, isJSON } from '@home/components/widget/lib/maps/maps-utils'; import {
createTooltip,
entitiesParseName,
isCutPolygon,
isJSON,
isValidLatLng
} from '@home/components/widget/lib/maps/maps-utils';
import { checkLngLat, createLoadingDiv } from '@home/components/widget/lib/maps/common-maps-utils'; import { checkLngLat, createLoadingDiv } from '@home/components/widget/lib/maps/common-maps-utils';
import { WidgetContext } from '@home/models/widget-component.models'; import { WidgetContext } from '@home/models/widget-component.models';
import { import {
@ -148,7 +154,8 @@ export default abstract class LeafletMap {
if (isDefinedAndNotNull(markerColor) && tinycolor(markerColor).isValid()) { if (isDefinedAndNotNull(markerColor) && tinycolor(markerColor).isValid()) {
const parsedColor = tinycolor(markerColor); const parsedColor = tinycolor(markerColor);
return L.divIcon({ return L.divIcon({
html: `<div style="background-color: ${parsedColor.setAlpha(0.4).toRgbString()};" class="marker-cluster tb-cluster-marker-element">` + html: `<div style="background-color: ${parsedColor.setAlpha(0.4).toRgbString()};" ` +
`class="marker-cluster tb-cluster-marker-element">` +
`<div style="background-color: ${parsedColor.setAlpha(0.9).toRgbString()};"><span>` + childCount + '</span></div></div>', `<div style="background-color: ${parsedColor.setAlpha(0.9).toRgbString()};"><span>` + childCount + '</span></div></div>',
iconSize: new L.Point(40, 40), iconSize: new L.Point(40, 40),
className: 'tb-cluster-marker-container' className: 'tb-cluster-marker-container'
@ -378,7 +385,8 @@ export default abstract class LeafletMap {
}); });
}, },
}); });
this.map.pm.Toolbar.changeControlOrder(['tbMarker', 'tbRectangle', 'tbPolygon', 'tbCircle', 'editMode', 'dragMode', 'tbCut', 'removalMode', 'rotateMode']); this.map.pm.Toolbar.changeControlOrder(['tbMarker', 'tbRectangle', 'tbPolygon', 'tbCircle',
'editMode', 'dragMode', 'tbCut', 'removalMode', 'rotateMode']);
} }
this.map.pm.setLang('en', this.translateService.instant('widgets.maps'), 'en'); this.map.pm.setLang('en', this.translateService.instant('widgets.maps'), 'en');
@ -643,19 +651,19 @@ export default abstract class LeafletMap {
} }
} }
extractPosition(data: FormattedData): {x: number, y: number} { extractPosition(data: FormattedData): {x: number; y: number} {
if (!data) { if (!data) {
return null; return null;
} }
const lat = data[this.options.latKeyName]; const lat = data[this.options.latKeyName];
const lng = data[this.options.lngKeyName]; const lng = data[this.options.lngKeyName];
if (!isDefinedAndNotNull(lat) || isString(lat) || isNaN(lat) || !isDefinedAndNotNull(lng) || isString(lng) || isNaN(lng)) { if (!isValidLatLng(lat, lng)) {
return null; return null;
} }
return {x: lat, y: lng}; return {x: lat, y: lng};
} }
positionToLatLng(position: {x: number, y: number}): L.LatLng { positionToLatLng(position: {x: number; y: number}): L.LatLng {
return L.latLng(position.x, position.y) as L.LatLng; return L.latLng(position.x, position.y) as L.LatLng;
} }
@ -936,7 +944,7 @@ export default abstract class LeafletMap {
return; return;
} }
this.saveLocation(data, this.convertToCustomFormat(e.target._latlng)).subscribe(); this.saveLocation(data, this.convertToCustomFormat(e.target._latlng)).subscribe();
} };
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: Partial<WidgetMarkersSettings>, private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: Partial<WidgetMarkersSettings>,
updateBounds = true, callback?, snappable = false): Marker { updateBounds = true, callback?, snappable = false): Marker {
@ -1141,7 +1149,7 @@ export default abstract class LeafletMap {
} }
} }
this.saveLocation(data, this.convertPolygonToCustomFormat(coordinates)).subscribe(() => {}); this.saveLocation(data, this.convertPolygonToCustomFormat(coordinates)).subscribe(() => {});
} };
createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: Partial<WidgetPolygonSettings>, createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: Partial<WidgetPolygonSettings>,
updateBounds = true, snappable = false) { updateBounds = true, snappable = false) {
@ -1218,7 +1226,7 @@ export default abstract class LeafletMap {
const center = e.layer.getLatLng(); const center = e.layer.getLatLng();
const radius = e.layer.getRadius(); const radius = e.layer.getRadius();
this.saveLocation(data, this.convertCircleToCustomFormat(center, radius)).subscribe(() => {}); this.saveLocation(data, this.convertCircleToCustomFormat(center, radius)).subscribe(() => {});
} };
updateCircle(circlesData: FormattedData[], updateBounds = true) { updateCircle(circlesData: FormattedData[], updateBounds = true) {
const toDelete = new Set(Array.from(this.circles.keys())); const toDelete = new Set(Array.from(this.circles.keys()));

View File

@ -20,7 +20,7 @@ import {
ShowTooltipAction, WidgetToolipSettings ShowTooltipAction, WidgetToolipSettings
} from './map-models'; } from './map-models';
import { Datasource, FormattedData } from '@app/shared/models/widget.models'; import { Datasource, FormattedData } from '@app/shared/models/widget.models';
import { fillDataPattern, processDataPattern, safeExecute } from '@core/utils'; import { fillDataPattern, isDefinedAndNotNull, isString, processDataPattern, safeExecute } from '@core/utils';
import { parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils'; import { parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils';
export function createTooltip(target: L.Layer, export function createTooltip(target: L.Layer,
@ -110,3 +110,16 @@ export function entitiesParseName(entities: FormattedData[], labelSettings: labe
} }
return entities; return entities;
} }
export const isValidLatitude = (latitude: any): boolean =>
isDefinedAndNotNull(latitude) &&
!isString(latitude) &&
!isNaN(latitude) && isFinite(latitude) && Math.abs(latitude) <= 90;
export const isValidLongitude = (longitude: any): boolean =>
isDefinedAndNotNull(longitude) &&
!isString(longitude) &&
!isNaN(longitude) && isFinite(longitude) && Math.abs(longitude) <= 180;
export const isValidLatLng = (latitude: any, longitude: any): boolean =>
isValidLatitude(latitude) && isValidLongitude(longitude);

View File

@ -80,6 +80,7 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor {
if (res) { if (res) {
this.modelValue = res; this.modelValue = res;
this.inputForm.control.patchValue({value: this.modelValue}); this.inputForm.control.patchValue({value: this.modelValue});
this.updateView();
} }
} }
); );