committed by
nickAS21
21 changed files with 1578 additions and 111 deletions
@ -0,0 +1,65 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m; |
|||
|
|||
import org.eclipse.californium.core.network.config.NetworkConfig; |
|||
import org.eclipse.leshan.client.object.Security; |
|||
|
|||
import static org.eclipse.leshan.client.object.Security.noSec; |
|||
|
|||
public class Lwm2mTestHelper { |
|||
|
|||
// Server
|
|||
public static final int SECURE_PORT = 5686; |
|||
public static final int SECURE_PORT_BS = 5688; |
|||
public static final String HOST = "localhost"; |
|||
public static final String HOST_BS = "localhost"; |
|||
public static final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT)); |
|||
public static final String ENDPOINT_SECURITY = "deviceAEndpoint"; |
|||
public static final String SECURE_URI = "coaps://localhost:" + SECURE_PORT; |
|||
|
|||
public static final int PORT = 5685; |
|||
public static final int PORT_BS = 5687; |
|||
public static final int SHORT_SERVER_ID = 123; |
|||
public static final int SHORT_SERVER_ID_BS = 111; |
|||
|
|||
public static final Security SECURITY = noSec("coap://localhost:" + PORT, SHORT_SERVER_ID); |
|||
public static final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); |
|||
|
|||
// Models
|
|||
public static final String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml", "5.xml", "6.xml", "9.xml", "19.xml", "3303.xml"}; |
|||
public static final int BINARY_APP_DATA_CONTAINER = 19; |
|||
public static final int TEMPERATURE_SENSOR = 3303; |
|||
|
|||
// Ids in Client
|
|||
public static final int objectInstanceId_0 = 0; |
|||
public static final int objectInstanceId_1 = 1; |
|||
public static final int objectInstanceId_12 = 12; |
|||
public static final int resourceId_0 = 0; |
|||
protected static final int resourceId_1 = 1; |
|||
protected static final int resourceId_2 = 2; |
|||
protected static final int resourceId_3 = 3; |
|||
public static final int resourceId_9 = 9; |
|||
protected static final int resourceId_11 = 11; |
|||
public static final int resourceId_14 = 14; |
|||
protected static final int resourceId_15= 15; |
|||
|
|||
public static final String resourceIdName_3_9 = "batteryLevel"; |
|||
public static final String resourceIdName_3_14 = "UtfOffset"; |
|||
public static final String resourceIdName_19_0_0 = "dataRead"; |
|||
public static final String resourceIdName_19_1_0 = "dataWrite"; |
|||
|
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.client; |
|||
|
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
@Slf4j |
|||
@Data |
|||
public class LwM2MLocationParams { |
|||
|
|||
private Float latitude; |
|||
private Float longitude; |
|||
private Float scaleFactor = 1.0F; |
|||
private final String locationPos = "50.4501:30.5234"; |
|||
private final Float locationScaleFactor = 1.0F; |
|||
|
|||
|
|||
protected void getPos() { |
|||
this.latitude = null; |
|||
this.longitude = null; |
|||
int c = locationPos.indexOf(':'); |
|||
this.latitude = Float.valueOf(locationPos.substring(0, c)); |
|||
this.longitude = Float.valueOf(locationPos.substring(c + 1)); |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,231 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.client; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; |
|||
import org.eclipse.leshan.client.servers.ServerIdentity; |
|||
import org.eclipse.leshan.core.model.ObjectModel; |
|||
import org.eclipse.leshan.core.model.ResourceModel; |
|||
import org.eclipse.leshan.core.node.LwM2mMultipleResource; |
|||
import org.eclipse.leshan.core.node.LwM2mResource; |
|||
import org.eclipse.leshan.core.response.ReadResponse; |
|||
import org.eclipse.leshan.core.response.WriteResponse; |
|||
|
|||
import javax.security.auth.Destroyable; |
|||
import java.sql.Time; |
|||
import java.util.Arrays; |
|||
import java.util.Date; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
|
|||
@Slf4j |
|||
public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements Destroyable { |
|||
|
|||
/** |
|||
* id = 0 |
|||
* Multiple |
|||
* base64 |
|||
*/ |
|||
|
|||
/** |
|||
* Example1: |
|||
* InNlcnZpY2VJZCI6Ik1ldGVyIiwNCiJzZXJ2aWNlRGF0YSI6ew0KImN1cnJlbnRSZWFka |
|||
* W5nIjoiNDYuMyIsDQoic2lnbmFsU3RyZW5ndGgiOjE2LA0KImRhaWx5QWN0aXZpdHlUaW1lIjo1NzA2DQo= |
|||
* "serviceId":"Meter", |
|||
* "serviceData":{ |
|||
* "currentReading":"46.3", |
|||
* "signalStrength":16, |
|||
* "dailyActivityTime":5706 |
|||
*/ |
|||
|
|||
/** |
|||
* Example2: |
|||
* InNlcnZpY2VJZCI6IldhdGVyTWV0ZXIiLA0KImNtZCI6IlNFVF9URU1QRVJBVFVSRV9SRUFEX |
|||
* 1BFUklPRCIsDQoicGFyYXMiOnsNCiJ2YWx1ZSI6NA0KICAgIH0sDQoNCg0K |
|||
* "serviceId":"WaterMeter", |
|||
* "cmd":"SET_TEMPERATURE_READ_PERIOD", |
|||
* "paras":{ |
|||
* "value":4 |
|||
* }, |
|||
*/ |
|||
// private String data = "InNlcnZpY2VJZCI6Ik1ldGVyIiwNCiJzZXJ2aWNlRGF0YSI6ew0KImN1cnJlbnRSZWFkaW5nIjoiNDYuMyIsDQoic2lnbmFsU3RyZW5ndGgiOjE2LA0KImRhaWx5QWN0aXZpdHlUaW1lIjo1NzA2DQo=";
|
|||
// private byte[] data;
|
|||
Map<Integer, byte[]> data; |
|||
private Integer priority = 0; |
|||
private Time timestamp; |
|||
private String description; |
|||
private String dataFormat; |
|||
private Integer appID = -1; |
|||
private static final List<Integer> supportedResources = Arrays.asList(0, 1, 2, 3, 4, 5); |
|||
|
|||
public LwM2mBinaryAppDataContainer() { |
|||
} |
|||
|
|||
public LwM2mBinaryAppDataContainer(ScheduledExecutorService executorService, Integer id) { |
|||
try { |
|||
if (id != null) this.setId(id); |
|||
executorService.scheduleWithFixedDelay(() -> |
|||
// fireResourcesChange(0, 2), 5000, 5000, TimeUnit.MILLISECONDS);
|
|||
fireResourcesChange(0, 2), 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN
|
|||
} catch (Throwable e) { |
|||
log.error("[{}]Throwable", e.toString()); |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public ReadResponse read(ServerIdentity identity, int resourceId) { |
|||
// log.warn("Read on Location resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId);
|
|||
try { |
|||
switch (resourceId) { |
|||
case 0: |
|||
// log.warn("Read on Location resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId);
|
|||
ReadResponse response = ReadResponse.success(resourceId, getData(), ResourceModel.Type.OPAQUE); |
|||
// log.warn("Response [{}]", response);
|
|||
return response; |
|||
|
|||
case 1: |
|||
return ReadResponse.success(resourceId, getPriority()); |
|||
case 2: |
|||
return ReadResponse.success(resourceId, getTimestamp()); |
|||
case 3: |
|||
return ReadResponse.success(resourceId, getDescription()); |
|||
case 4: |
|||
return ReadResponse.success(resourceId, getDataFormat()); |
|||
case 5: |
|||
return ReadResponse.success(resourceId, getAppID()); |
|||
default: |
|||
return super.read(identity, resourceId); |
|||
} |
|||
} catch (Exception e) { |
|||
return ReadResponse.badRequest(e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) { |
|||
log.info("Write on Device resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); |
|||
switch (resourceId) { |
|||
case 0: |
|||
if (setData(value, replace)) { |
|||
return WriteResponse.success(); |
|||
} else { |
|||
WriteResponse.badRequest("Invalidate value ..."); |
|||
} |
|||
case 1: |
|||
setPriority((Integer) (value.getValue() instanceof Long ? ((Long) value.getValue()).intValue() : value.getValue())); |
|||
fireResourcesChange(resourceId); |
|||
return WriteResponse.success(); |
|||
case 2: |
|||
setTimestamp(((Date) value.getValue()).getTime()); |
|||
fireResourcesChange(resourceId); |
|||
return WriteResponse.success(); |
|||
case 3: |
|||
setDescription((String) value.getValue()); |
|||
fireResourcesChange(resourceId); |
|||
return WriteResponse.success(); |
|||
case 4: |
|||
setDataFormat((String) value.getValue()); |
|||
fireResourcesChange(resourceId); |
|||
return WriteResponse.success(); |
|||
case 5: |
|||
setAppID((Integer) value.getValue()); |
|||
fireResourcesChange(resourceId); |
|||
return WriteResponse.success(); |
|||
default: |
|||
return super.write(identity, replace, resourceId, value); |
|||
} |
|||
} |
|||
|
|||
private Integer getAppID() { |
|||
return this.appID; |
|||
} |
|||
|
|||
private void setAppID(Integer appId) { |
|||
this.appID = appId; |
|||
} |
|||
|
|||
private void setDataFormat(String value) { |
|||
this.dataFormat = value; |
|||
} |
|||
|
|||
private String getDataFormat() { |
|||
// return this.dataFormat == null ? "base64" : this.dataFormat;
|
|||
return this.dataFormat == null ? "OPAQUE" : this.dataFormat; |
|||
} |
|||
|
|||
private void setDescription(String value) { |
|||
this.description = value; |
|||
} |
|||
|
|||
private String getDescription() { |
|||
return this.description == null ? "meter reading" : this.description; |
|||
} |
|||
|
|||
private void setTimestamp(long time) { |
|||
this.timestamp = new Time(time); |
|||
} |
|||
|
|||
private Time getTimestamp() { |
|||
return this.timestamp != null ? this.timestamp : new Time(new Date().getTime()); |
|||
} |
|||
|
|||
// fireResourcesChange(resourceId);
|
|||
private boolean setData(LwM2mResource value, boolean replace) { |
|||
try { |
|||
if (value instanceof LwM2mMultipleResource) { |
|||
if (replace || this.data == null) { |
|||
this.data = new HashMap<>(); |
|||
} |
|||
value.getInstances().values().forEach(v -> { |
|||
this.data.put(v.getId(), (byte[]) v.getValue()); |
|||
}); |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} catch (Exception e) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
private Map<Integer, byte[]> getData() { |
|||
// this.data.put(23, new byte[]{0,0, 2,3});
|
|||
return data; |
|||
} |
|||
|
|||
@Override |
|||
public List<Integer> getAvailableResourceIds(ObjectModel model) { |
|||
return supportedResources; |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
} |
|||
|
|||
private int getPriority() { |
|||
return this.priority; |
|||
} |
|||
|
|||
private void setPriority(int value) { |
|||
this.priority = value; |
|||
} |
|||
} |
|||
@ -0,0 +1,150 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.client; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; |
|||
import org.eclipse.leshan.client.servers.ServerIdentity; |
|||
import org.eclipse.leshan.core.model.ObjectModel; |
|||
import org.eclipse.leshan.core.response.ReadResponse; |
|||
|
|||
import javax.security.auth.Destroyable; |
|||
import java.util.Arrays; |
|||
import java.util.Date; |
|||
import java.util.List; |
|||
import java.util.Random; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Slf4j |
|||
public class LwM2mLocation extends BaseInstanceEnabler implements Destroyable { |
|||
|
|||
private float latitude; |
|||
private float longitude; |
|||
private float scaleFactor; |
|||
private Date timestamp; |
|||
protected static final Random RANDOM = new Random(); |
|||
private static final List<Integer> supportedResources = Arrays.asList(0, 1, 5); |
|||
|
|||
public LwM2mLocation() { |
|||
this(null, null, 1.0f); |
|||
} |
|||
|
|||
public LwM2mLocation(Float latitude, Float longitude, float scaleFactor) { |
|||
|
|||
if (latitude != null) { |
|||
this.latitude = latitude + 90f; |
|||
} else { |
|||
this.latitude = RANDOM.nextInt(180); |
|||
} |
|||
if (longitude != null) { |
|||
this.longitude = longitude + 180f; |
|||
} else { |
|||
this.longitude = RANDOM.nextInt(360); |
|||
} |
|||
this.scaleFactor = scaleFactor; |
|||
timestamp = new Date(); |
|||
} |
|||
|
|||
public LwM2mLocation(Float latitude, Float longitude, float scaleFactor, ScheduledExecutorService executorService, Integer id) { |
|||
try { |
|||
if (id != null) this.setId(id); |
|||
if (latitude != null) { |
|||
this.latitude = latitude + 90f; |
|||
} else { |
|||
this.latitude = RANDOM.nextInt(180); |
|||
} |
|||
if (longitude != null) { |
|||
this.longitude = longitude + 180f; |
|||
} else { |
|||
this.longitude = RANDOM.nextInt(360); |
|||
} |
|||
this.scaleFactor = scaleFactor; |
|||
timestamp = new Date(); |
|||
executorService.scheduleWithFixedDelay(() -> |
|||
fireResourcesChange(0, 1), 10000, 10000, TimeUnit.MILLISECONDS); |
|||
} catch (Throwable e) { |
|||
log.error("[{}]Throwable", e.toString()); |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public ReadResponse read(ServerIdentity identity, int resourceId) { |
|||
log.info("Read on Location resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); |
|||
switch (resourceId) { |
|||
case 0: |
|||
return ReadResponse.success(resourceId, getLatitude()); |
|||
case 1: |
|||
return ReadResponse.success(resourceId, getLongitude()); |
|||
case 5: |
|||
return ReadResponse.success(resourceId, getTimestamp()); |
|||
default: |
|||
return super.read(identity, resourceId); |
|||
} |
|||
} |
|||
|
|||
public void moveLocation(String nextMove) { |
|||
switch (nextMove.charAt(0)) { |
|||
case 'w': |
|||
moveLatitude(1.0f); |
|||
// log.info("Move to North [{}]/[{}]", getLatitude(), getLongitude());
|
|||
break; |
|||
case 'a': |
|||
moveLongitude(-1.0f); |
|||
// log.info("Move to East [{}]/[{}]", getLatitude(), getLongitude());
|
|||
break; |
|||
case 's': |
|||
moveLatitude(-1.0f); |
|||
// log.info("Move to South [{}]/[{}]", getLatitude(), getLongitude());
|
|||
break; |
|||
case 'd': |
|||
moveLongitude(1.0f); |
|||
// log.info("Move to West [{}]/[{}]", getLatitude(), getLongitude());
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
private void moveLatitude(float delta) { |
|||
latitude = latitude + delta * scaleFactor; |
|||
timestamp = new Date(); |
|||
fireResourcesChange(0, 5); |
|||
} |
|||
|
|||
private void moveLongitude(float delta) { |
|||
longitude = longitude + delta * scaleFactor; |
|||
timestamp = new Date(); |
|||
fireResourcesChange(1, 5); |
|||
} |
|||
|
|||
public float getLatitude() { |
|||
return latitude - 90.0f; |
|||
} |
|||
|
|||
public float getLongitude() { |
|||
return longitude - 180.f; |
|||
} |
|||
|
|||
public Date getTimestamp() { |
|||
return timestamp; |
|||
} |
|||
|
|||
@Override |
|||
public List<Integer> getAvailableResourceIds(ObjectModel model) { |
|||
return supportedResources; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.client; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.leshan.client.resource.BaseInstanceEnabler; |
|||
import org.eclipse.leshan.client.servers.ServerIdentity; |
|||
import org.eclipse.leshan.core.model.ObjectModel; |
|||
import org.eclipse.leshan.core.response.ExecuteResponse; |
|||
import org.eclipse.leshan.core.response.ReadResponse; |
|||
|
|||
import javax.security.auth.Destroyable; |
|||
import java.math.BigDecimal; |
|||
import java.math.RoundingMode; |
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
import java.util.Random; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Slf4j |
|||
public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destroyable { |
|||
|
|||
private static final String UNIT_CELSIUS = "cel"; |
|||
private double currentTemp = 20d; |
|||
private double minMeasuredValue = currentTemp; |
|||
private double maxMeasuredValue = currentTemp; |
|||
protected static final Random RANDOM = new Random(); |
|||
private static final List<Integer> supportedResources = Arrays.asList(5601, 5602, 5700, 5701); |
|||
|
|||
public LwM2mTemperatureSensor() { |
|||
|
|||
} |
|||
|
|||
public LwM2mTemperatureSensor(ScheduledExecutorService executorService, Integer id) { |
|||
try { |
|||
if (id != null) this.setId(id); |
|||
executorService.scheduleWithFixedDelay(this::adjustTemperature, 2000, 2000, TimeUnit.MILLISECONDS); |
|||
} catch (Throwable e) { |
|||
log.error("[{}]Throwable", e.toString()); |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public synchronized ReadResponse read(ServerIdentity identity, int resourceId) { |
|||
log.info("Read on Temperature resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); |
|||
switch (resourceId) { |
|||
case 5601: |
|||
return ReadResponse.success(resourceId, getTwoDigitValue(minMeasuredValue)); |
|||
case 5602: |
|||
return ReadResponse.success(resourceId, getTwoDigitValue(maxMeasuredValue)); |
|||
case 5700: |
|||
return ReadResponse.success(resourceId, getTwoDigitValue(currentTemp)); |
|||
case 5701: |
|||
return ReadResponse.success(resourceId, UNIT_CELSIUS); |
|||
default: |
|||
return super.read(identity, resourceId); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public synchronized ExecuteResponse execute(ServerIdentity identity, int resourceId, String params) { |
|||
log.info("Execute on Temperature resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); |
|||
switch (resourceId) { |
|||
case 5605: |
|||
resetMinMaxMeasuredValues(); |
|||
return ExecuteResponse.success(); |
|||
default: |
|||
return super.execute(identity, resourceId, params); |
|||
} |
|||
} |
|||
|
|||
private double getTwoDigitValue(double value) { |
|||
BigDecimal toBeTruncated = BigDecimal.valueOf(value); |
|||
return toBeTruncated.setScale(2, RoundingMode.HALF_UP).doubleValue(); |
|||
} |
|||
|
|||
private void adjustTemperature() { |
|||
float delta = (RANDOM.nextInt(20) - 10) / 10f; |
|||
currentTemp += delta; |
|||
Integer changedResource = adjustMinMaxMeasuredValue(currentTemp); |
|||
if (changedResource != null) { |
|||
fireResourcesChange(5700, changedResource); |
|||
} else { |
|||
fireResourcesChange(5700); |
|||
} |
|||
} |
|||
|
|||
private synchronized Integer adjustMinMaxMeasuredValue(double newTemperature) { |
|||
if (newTemperature > maxMeasuredValue) { |
|||
maxMeasuredValue = newTemperature; |
|||
return 5602; |
|||
} else if (newTemperature < minMeasuredValue) { |
|||
minMeasuredValue = newTemperature; |
|||
return 5601; |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
private void resetMinMaxMeasuredValues() { |
|||
minMeasuredValue = currentTemp; |
|||
maxMeasuredValue = currentTemp; |
|||
} |
|||
|
|||
@Override |
|||
public List<Integer> getAvailableResourceIds(ObjectModel model) { |
|||
return supportedResources; |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,307 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.rpc; |
|||
|
|||
import com.fasterxml.jackson.core.type.TypeReference; |
|||
import org.apache.commons.io.IOUtils; |
|||
import org.junit.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.springframework.util.SocketUtils; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.common.util.ThingsBoardThreadFactory; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
|||
import org.thingsboard.server.common.data.DeviceProfileType; |
|||
import org.thingsboard.server.common.data.DeviceTransportType; |
|||
import org.thingsboard.server.common.data.ResourceType; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials; |
|||
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; |
|||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
|||
import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
|||
import org.thingsboard.server.controller.AbstractWebsocketTest; |
|||
import org.thingsboard.server.controller.TbTestWebSocketClient; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig; |
|||
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapServers; |
|||
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MServerBootstrap; |
|||
import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; |
|||
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; |
|||
import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; |
|||
|
|||
import java.util.Base64; |
|||
import java.util.Set; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.function.Predicate; |
|||
|
|||
import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; |
|||
import static org.eclipse.leshan.core.LwM2mId.DEVICE; |
|||
import static org.eclipse.leshan.core.LwM2mId.FIRMWARE; |
|||
import static org.eclipse.leshan.core.LwM2mId.SERVER; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST_BS; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT_BS; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_PORT; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURE_PORT_BS; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.HOST; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.PORT; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SHORT_SERVER_ID_BS; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_0_0; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_1_0; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_14; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_9; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources; |
|||
|
|||
@DaoSqlTest |
|||
public class RpcAbstractLwM2MIntegrationTest extends AbstractWebsocketTest { |
|||
|
|||
protected final String RPC_TRANSPORT_CONFIGURATION = "{\n" + |
|||
" \"type\": \"LWM2M\",\n" + |
|||
" \"observeAttr\": {\n" + |
|||
" \"keyName\": {\n" + |
|||
" \"" + "/" + DEVICE + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\": \"" + resourceIdName_3_9 + "\",\n" + |
|||
" \"" + "/" + DEVICE + "/" + objectInstanceId_0 + "/" + resourceId_14 + "\": \"" + resourceIdName_3_14 + "\",\n" + |
|||
" \"" + "/" + BINARY_APP_DATA_CONTAINER + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\": \"" + resourceIdName_19_0_0 + "\",\n" + |
|||
" \"" + "/" + BINARY_APP_DATA_CONTAINER + "/" + objectInstanceId_1 + "/" + resourceId_0 + "\": \"" + resourceIdName_19_1_0 + "\"\n" + |
|||
" },\n" + |
|||
" \"observe\": [\n" + |
|||
" \"" + "/" + DEVICE + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\",\n" + |
|||
" \"" + "/" + BINARY_APP_DATA_CONTAINER + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\"\n" + |
|||
" ],\n" + |
|||
" \"attribute\": [\n" + |
|||
" ],\n" + |
|||
" \"telemetry\": [\n" + |
|||
" \"" + "/" + DEVICE + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\",\n" + |
|||
" \"" + "/" + DEVICE + "/" + objectInstanceId_0 + "/" + resourceId_14 + "\",\n" + |
|||
" \"" + "/" + BINARY_APP_DATA_CONTAINER + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\",\n" + |
|||
" \"" + "/" + BINARY_APP_DATA_CONTAINER + "/" + objectInstanceId_1 + "/" + resourceId_0 + "\"\n" + |
|||
" ],\n" + |
|||
" \"attributeLwm2m\": {}\n" + |
|||
" },\n" + |
|||
" \"bootstrap\": {\n" + |
|||
" \"servers\": {\n" + |
|||
" \"binding\": \"U\",\n" + |
|||
" \"shortId\": 123,\n" + |
|||
" \"lifetime\": 300,\n" + |
|||
" \"notifIfDisabled\": true,\n" + |
|||
" \"defaultMinPeriod\": 1\n" + |
|||
" },\n" + |
|||
" \"lwm2mServer\": {\n" + |
|||
" \"host\": \"localhost\",\n" + |
|||
" \"port\": 5686,\n" + |
|||
" \"serverId\": 123,\n" + |
|||
" \"serverPublicKey\": \"\",\n" + |
|||
" \"bootstrapServerIs\": false,\n" + |
|||
" \"clientHoldOffTime\": 1,\n" + |
|||
" \"bootstrapServerAccountTimeout\": 0\n" + |
|||
" },\n" + |
|||
" \"bootstrapServer\": {\n" + |
|||
" \"host\": \"localhost\",\n" + |
|||
" \"port\": 5687,\n" + |
|||
" \"serverId\": 111,\n" + |
|||
" \"securityMode\": \"NO_SEC\",\n" + |
|||
" \"serverPublicKey\": \"\",\n" + |
|||
" \"bootstrapServerIs\": true,\n" + |
|||
" \"clientHoldOffTime\": 1,\n" + |
|||
" \"bootstrapServerAccountTimeout\": 0\n" + |
|||
" }\n" + |
|||
" },\n" + |
|||
" \"clientLwM2mSettings\": {\n" + |
|||
" \"clientOnlyObserveAfterConnect\": 1,\n" + |
|||
" \"fwUpdateStrategy\": 1,\n" + |
|||
" \"swUpdateStrategy\": 1\n" + |
|||
" }\n" + |
|||
"}"; |
|||
|
|||
protected static final String ENDPOINT_RPC = "deviceEndpointRpc"; |
|||
protected ScheduledExecutorService executor; |
|||
protected TbTestWebSocketClient wsClient; |
|||
protected DeviceProfile deviceProfile; |
|||
|
|||
protected NoSecClientCredentials clientCredentials; |
|||
protected LwM2MTestClient client; |
|||
protected String deviceId; |
|||
public Set expectedObjects; |
|||
public Set expectedObjectIdVers; |
|||
public Set expectedInstances; |
|||
public Set expectedObjectIdVerInstances; |
|||
|
|||
protected String objectInstanceIdVer_1; |
|||
protected String objectIdVer_2; |
|||
private static final Predicate predicate_3 = path -> (!((String) path).contains("/" + TEMPERATURE_SENSOR) && ((String) path).contains("/" + DEVICE)); |
|||
protected String objectIdVer_3; |
|||
protected String objectInstanceIdVer_3; |
|||
protected String objectInstanceIdVer_5; |
|||
protected String objectIdVer_19; |
|||
|
|||
public RpcAbstractLwM2MIntegrationTest(){ } |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
executor = Executors.newScheduledThreadPool(10, ThingsBoardThreadFactory.forName("test-lwm2m-rpc-scheduled")); |
|||
loginTenantAdmin(); |
|||
wsClient = buildAndConnectWebSocketClient(); |
|||
createDeviceProfile(RPC_TRANSPORT_CONFIGURATION); |
|||
clientCredentials = new NoSecClientCredentials(); |
|||
clientCredentials.setEndpoint(ENDPOINT_RPC); |
|||
Device device = createDevice(clientCredentials); |
|||
deviceId = device.getId().getId().toString(); |
|||
client = new LwM2MTestClient(executor, ENDPOINT_RPC); |
|||
int clientPort = SocketUtils.findAvailableTcpPort(); |
|||
client.init(SECURITY, COAP_CONFIG, clientPort); |
|||
for (String resourceName : resources) { |
|||
TbResource lwModel = new TbResource(); |
|||
lwModel.setResourceType(ResourceType.LWM2M_MODEL); |
|||
lwModel.setTitle(resourceName); |
|||
lwModel.setFileName(resourceName); |
|||
lwModel.setTenantId(tenantId); |
|||
byte[] bytes = IOUtils.toByteArray(AbstractLwM2MIntegrationTest.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName)); |
|||
lwModel.setData(Base64.getEncoder().encodeToString(bytes)); |
|||
lwModel = doPostWithTypedResponse("/api/resource", lwModel, new TypeReference<>() { |
|||
}); |
|||
Assert.assertNotNull(lwModel); |
|||
} |
|||
wsClient = buildAndConnectWebSocketClient(); |
|||
expectedObjects = ConcurrentHashMap.newKeySet(); |
|||
expectedObjectIdVers = ConcurrentHashMap.newKeySet(); |
|||
expectedInstances = ConcurrentHashMap.newKeySet(); |
|||
expectedObjectIdVerInstances = ConcurrentHashMap.newKeySet(); |
|||
client.getClient().getObjectTree().getObjectEnablers().forEach((key, val) -> { |
|||
if (key > 0) { |
|||
String objectVerId = "/" + key; |
|||
if (!val.getObjectModel().version.equals("1.0")) { |
|||
objectVerId += ("_" + val.getObjectModel().version); |
|||
} |
|||
expectedObjects.add("/" + key); |
|||
expectedObjectIdVers.add(objectVerId); |
|||
String finalObjectVerId = objectVerId; |
|||
val.getAvailableInstanceIds().forEach(inststanceId -> { |
|||
expectedInstances.add("/" + key + "/" + inststanceId); |
|||
expectedObjectIdVerInstances.add(finalObjectVerId + "/" + inststanceId); |
|||
}); |
|||
} |
|||
}); |
|||
objectInstanceIdVer_1 = (String) expectedObjectIdVerInstances.stream().filter(path -> (!((String) path).contains("/" + BINARY_APP_DATA_CONTAINER) && ((String) path).contains("/" + SERVER))).findFirst().get(); |
|||
objectIdVer_2 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + ACCESS_CONTROL)).findFirst().get(); |
|||
objectIdVer_3 = (String) expectedObjects.stream().filter(predicate_3).findFirst().get(); |
|||
objectIdVer_19 = (String) expectedObjects.stream().filter(predicate_3).findFirst().get(); |
|||
objectInstanceIdVer_3 = (String) expectedObjectIdVerInstances.stream().filter(predicate_3).findFirst().get(); |
|||
objectInstanceIdVer_5 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + FIRMWARE)).findFirst().get(); |
|||
objectIdVer_19 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + BINARY_APP_DATA_CONTAINER)).findFirst().get(); |
|||
} |
|||
|
|||
protected void createDeviceProfile(String transportConfiguration) throws Exception { |
|||
deviceProfile = new DeviceProfile(); |
|||
|
|||
deviceProfile.setName("LwM2M_RPC"); |
|||
deviceProfile.setType(DeviceProfileType.DEFAULT); |
|||
deviceProfile.setTenantId(tenantId); |
|||
deviceProfile.setTransportType(DeviceTransportType.LWM2M); |
|||
deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); |
|||
deviceProfile.setDescription(deviceProfile.getName()); |
|||
|
|||
DeviceProfileData deviceProfileData = new DeviceProfileData(); |
|||
deviceProfileData.setConfiguration(new DefaultDeviceProfileConfiguration()); |
|||
deviceProfileData.setProvisionConfiguration(new DisabledDeviceProfileProvisionConfiguration(null)); |
|||
deviceProfileData.setTransportConfiguration(JacksonUtil.fromString(transportConfiguration, Lwm2mDeviceProfileTransportConfiguration.class)); |
|||
deviceProfile.setProfileData(deviceProfileData); |
|||
|
|||
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
|||
Assert.assertNotNull(deviceProfile); |
|||
} |
|||
|
|||
|
|||
protected Device createDevice(LwM2MClientCredentials clientCredentials) throws Exception { |
|||
Device device = new Device(); |
|||
device.setName("Device A"); |
|||
device.setDeviceProfileId(deviceProfile.getId()); |
|||
device.setTenantId(tenantId); |
|||
device = doPost("/api/device", device, Device.class); |
|||
Assert.assertNotNull(device); |
|||
DeviceCredentials deviceCredentials = |
|||
doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class); |
|||
Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId()); |
|||
deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); |
|||
LwM2MCredentials credentials = new LwM2MCredentials(); |
|||
credentials.setClient(clientCredentials); |
|||
credentials.setBootstrap(createBootstrapConfig()); |
|||
deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials)); |
|||
doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); |
|||
return device; |
|||
} |
|||
|
|||
protected LwM2MBootstrapConfig createBootstrapConfig() { |
|||
LwM2MBootstrapConfig bootstrap = new LwM2MBootstrapConfig(); |
|||
LwM2MBootstrapServers servers = new LwM2MBootstrapServers(); |
|||
servers.setShortId(SHORT_SERVER_ID); |
|||
bootstrap.setServers(servers); |
|||
LwM2MServerBootstrap server = new LwM2MServerBootstrap(); |
|||
server.setHost(HOST); |
|||
server.setPort(PORT); |
|||
server.setSecurityHost(HOST); |
|||
server.setSecurityPort(SECURE_PORT); |
|||
server.setServerId(servers.getShortId()); |
|||
server.setBootstrapServerIs(false); |
|||
bootstrap.setLwm2mServer(server); |
|||
LwM2MServerBootstrap serverBS = new LwM2MServerBootstrap(); |
|||
serverBS.setHost(HOST_BS); |
|||
serverBS.setPort(PORT_BS); |
|||
serverBS.setSecurityHost(HOST_BS); |
|||
serverBS.setSecurityPort(SECURE_PORT_BS); |
|||
serverBS.setServerId(SHORT_SERVER_ID_BS); |
|||
serverBS.setBootstrapServerIs(true); |
|||
bootstrap.setBootstrapServer(serverBS); |
|||
return bootstrap; |
|||
} |
|||
|
|||
protected String objectIdVerToObjectId(String objectIdVer) { |
|||
return objectIdVer.contains("_") ? objectIdVer.split("_")[0] : objectIdVer; |
|||
} |
|||
|
|||
protected String objectInstanceIdVerToObjectInstanceId(String objectInstanceIdVer) { |
|||
String[] objectIdVer = objectInstanceIdVer.split("/"); |
|||
return objectIdVer[1].contains("_") ? objectIdVer[1].split("_")[0] + "/" + objectIdVer[2] : objectInstanceIdVer; |
|||
} |
|||
|
|||
@After |
|||
public void after() { |
|||
if (client != null) { |
|||
client.destroy(); |
|||
} |
|||
executor.shutdownNow(); |
|||
if (wsClient != null) { |
|||
wsClient.close(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.rpc.sql; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.eclipse.leshan.core.ResponseCode; |
|||
import org.junit.Test; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.transport.lwm2m.rpc.RpcAbstractLwM2MIntegrationTest; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertTrue; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0; |
|||
|
|||
|
|||
public class RpcLwm2mIntegrationCreateTest extends RpcAbstractLwM2MIntegrationTest { |
|||
|
|||
|
|||
/** |
|||
* Create {"id":"/19_1.1","value":{"0":{"0":"00AC"}, "1":1}} |
|||
* |
|||
* create_2_instances_in_object |
|||
* new ObjectInstance if Object is Multiple & Resource Single |
|||
* Create {"id":"/19_1.1/0","value":{"0":{"0":"00AC", "1":1}}} |
|||
* {"result":"BAD_REQUEST","error":"instance 0 already exists"} |
|||
* |
|||
*/ |
|||
@Test |
|||
public void testCreateObjectInstanceWithInstanceIdAlreadyExistsByIdKey_Result_BAD_REQUEST() throws Exception { |
|||
String expectedPath = objectIdVer_19 + "/" + objectInstanceId_0; |
|||
String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}, \"1\":1}"; |
|||
String actualResult = sendRPCreateById(expectedPath, expectedValue); |
|||
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); |
|||
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); |
|||
String expected = "instance " + objectInstanceId_0 + " already exists"; |
|||
String actual = rpcActualResult.get("error").asText(); |
|||
assertTrue(actual.equals(expected)); |
|||
} |
|||
|
|||
/** |
|||
* failed: cannot_create_mandatory_single_object |
|||
* Create {"id":"/3/2","value":{"0":"00AC"}} |
|||
*/ |
|||
|
|||
|
|||
/** |
|||
* failed: cannot_create_instance_of_security_object |
|||
* Create {"id":"/0/2","value":{"0":"00AC"}} |
|||
*/ |
|||
|
|||
/** |
|||
* failed: cannot_create_instance_of_absent_object |
|||
* Create {"id":"/50/2","value":{"0":"00AC"}} |
|||
*/ |
|||
|
|||
|
|||
private String sendRPCreateById(String path, String value) throws Exception { |
|||
String setRpcRequest = "{\"method\": \"Create\", \"params\": {\"id\": \"" + path + "\", \"value\": " + value + " }}"; |
|||
return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.security.sql; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; |
|||
import org.thingsboard.server.transport.lwm2m.security.AbstractLwM2MIntegrationTest; |
|||
|
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG; |
|||
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY; |
|||
|
|||
@Slf4j |
|||
public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
|||
|
|||
@Test |
|||
public void testConnectAndObserveTelemetry() throws Exception { |
|||
NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); |
|||
clientCredentials.setEndpoint(ENDPOINT); |
|||
super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, ENDPOINT); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,144 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
FILE INFORMATION |
|||
|
|||
OMA Permanent Document |
|||
File: OMA-SUP-LwM2M_BinaryAppDataContainer-V1_0_1-20190221-A |
|||
Type: xml |
|||
|
|||
Public Reachable Information |
|||
Path: http://www.openmobilealliance.org/tech/profiles |
|||
Name: LwM2M_BinaryAppDataContainer-v1_0_1.xml |
|||
|
|||
NORMATIVE INFORMATION |
|||
|
|||
Information about this file can be found in the latest revision of |
|||
|
|||
OMA-TS-LWM2M_BinaryAppDataContainer-V1_0_1 |
|||
|
|||
This is available at http://www.openmobilealliance.org/ |
|||
|
|||
Send comments to https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues |
|||
|
|||
CHANGE HISTORY |
|||
|
|||
15062018 Status changed to Approved by DM, Doc Ref # OMA-DM&SE-2018-0061-INP_LWM2M_APPDATA_V1_0_ERP_for_final_Approval |
|||
21022019 Status changed to Approved by IPSO, Doc Ref # OMA-IPSO-2019-0025-INP_LwM2M_Object_App_Data_Container_1_0_1_for_Final_Approval |
|||
|
|||
LEGAL DISCLAIMER |
|||
|
|||
Copyright 2019 Open Mobile Alliance. |
|||
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
1. Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
2. Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
3. Neither the name of the copyright holder nor the names of its |
|||
contributors may be used to endorse or promote products derived |
|||
from this software without specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|||
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|||
POSSIBILITY OF SUCH DAMAGE. |
|||
|
|||
The above license is used as a license under copyright only. Please |
|||
reference the OMA IPR Policy for patent licensing terms: |
|||
https://www.omaspecworks.org/about/intellectual-property-rights/ |
|||
|
|||
--> |
|||
<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.openmobilealliance.org/tech/profiles/LWM2M-v1_1.xsd"> |
|||
<Object ObjectType="MODefinition"> |
|||
<Name>BinaryAppDataContainer</Name> |
|||
<Description1><![CDATA[This LwM2M Objects provides the application service data related to a LwM2M Server, eg. Water meter data. |
|||
There are several methods to create instance to indicate the message direction based on the negotiation between Application and LwM2M. The Client and Server should negotiate the instance(s) used to exchange the data. For example: |
|||
- Using a single instance for both directions communication, from Client to Server and from Server to Client. |
|||
- Using an instance for communication from Client to Server and another one for communication from Server to Client |
|||
- Using several instances |
|||
]]></Description1> |
|||
<ObjectID>19</ObjectID> |
|||
<ObjectURN>urn:oma:lwm2m:oma:19:1.1</ObjectURN> |
|||
<LWM2MVersion>1.1</LWM2MVersion> |
|||
<ObjectVersion>1.1</ObjectVersion> |
|||
<MultipleInstances>Multiple</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Resources> |
|||
<Item ID="0"><Name>Data</Name> |
|||
<Operations>RW</Operations> |
|||
<MultipleInstances>Multiple</MultipleInstances> |
|||
<Mandatory>Mandatory</Mandatory> |
|||
<Type>Opaque</Type> |
|||
<RangeEnumeration /> |
|||
<Units /> |
|||
<Description><![CDATA[Indicates the application data content.]]></Description> |
|||
</Item> |
|||
<Item ID="1"><Name>Data Priority</Name> |
|||
<Operations>RW</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Integer</Type> |
|||
<RangeEnumeration>1 bytes</RangeEnumeration> |
|||
<Units /> |
|||
<Description><![CDATA[Indicates the Application data priority: |
|||
0:Immediate |
|||
1:BestEffort |
|||
2:Latest |
|||
3-100: Reserved for future use. |
|||
101-254: Proprietary mode.]]></Description> |
|||
</Item> |
|||
<Item ID="2"><Name>Data Creation Time</Name> |
|||
<Operations>RW</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Time</Type> |
|||
<RangeEnumeration /> |
|||
<Units /> |
|||
<Description><![CDATA[Indicates the Data instance creation timestamp.]]></Description> |
|||
</Item> |
|||
<Item ID="3"><Name>Data Description</Name> |
|||
<Operations>RW</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>String</Type> |
|||
<RangeEnumeration>32 bytes</RangeEnumeration> |
|||
<Units /> |
|||
<Description><![CDATA[Indicates the data description. |
|||
e.g. "meter reading".]]></Description> |
|||
</Item> |
|||
<Item ID="4"><Name>Data Format</Name> |
|||
<Operations>RW</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>String</Type> |
|||
<RangeEnumeration>32 bytes</RangeEnumeration> |
|||
<Units /> |
|||
<Description><![CDATA[Indicates the format of the Application Data. |
|||
e.g. YG-Meter-Water-Reading |
|||
UTF8-string |
|||
]]></Description> |
|||
</Item> |
|||
<Item ID="5"><Name>App ID</Name> |
|||
<Operations>RW</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Integer</Type> |
|||
<RangeEnumeration>2 bytes</RangeEnumeration> |
|||
<Units /> |
|||
<Description><![CDATA[Indicates the destination Application ID.]]></Description> |
|||
</Item></Resources> |
|||
<Description2><![CDATA[]]></Description2> |
|||
</Object> |
|||
</LWM2M> |
|||
@ -0,0 +1,103 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
|
|||
Copyright © 2016-2018 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://openmobilealliance.org/tech/profiles/LWM2M.xsd"> |
|||
<Object ObjectType="MODefinition"> |
|||
<Name>Temperature</Name> |
|||
<Description1>This IPSO object should be used with a temperature sensor to report a temperature measurement. It also provides resources for minimum/maximum measured values and the minimum/maximum range that can be measured by the temperature sensor. An example measurement unit is degrees Celsius.</Description1> |
|||
<ObjectID>3303</ObjectID> |
|||
<ObjectURN>urn:oma:lwm2m:ext:3303</ObjectURN> |
|||
<LWM2MVersion>1.0</LWM2MVersion> |
|||
<ObjectVersion>1.0</ObjectVersion> |
|||
<MultipleInstances>Multiple</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Resources> |
|||
<Item ID="5700"> |
|||
<Name>Sensor Value</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Mandatory</Mandatory> |
|||
<Type>Float</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description>Last or Current Measured Value from the Sensor</Description> |
|||
</Item> |
|||
<Item ID="5601"> |
|||
<Name>Min Measured Value</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Float</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description>The minimum value measured by the sensor since power ON or reset</Description> |
|||
</Item> |
|||
<Item ID="5602"> |
|||
<Name>Max Measured Value</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Float</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description>The maximum value measured by the sensor since power ON or reset</Description> |
|||
</Item> |
|||
<Item ID="5603"> |
|||
<Name>Min Range Value</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Float</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description>The minimum value that can be measured by the sensor</Description> |
|||
</Item> |
|||
<Item ID="5604"> |
|||
<Name>Max Range Value</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Float</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description>The maximum value that can be measured by the sensor</Description> |
|||
</Item> |
|||
<Item ID="5701"> |
|||
<Name>Sensor Units</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>String</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description>Measurement Units Definition.</Description> |
|||
</Item> |
|||
<Item ID="5605"> |
|||
<Name>Reset Min and Max Measured Values</Name> |
|||
<Operations>E</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type></Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description>Reset the Min and Max Measured Values to Current Value</Description> |
|||
</Item> |
|||
</Resources> |
|||
<Description2></Description2> |
|||
</Object> |
|||
</LWM2M> |
|||
@ -0,0 +1,143 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
|
|||
<!-- |
|||
FILE INFORMATION |
|||
|
|||
OMA Permanent Document |
|||
File: OMA-SUP-XML_6-V1_0-20201110-A.xml |
|||
http://www.openmobilealliance.org/release/ObjLwM2M_Location/ |
|||
|
|||
OMNA LwM2M Registry |
|||
Path: https://github.com/OpenMobileAlliance/lwm2m-registry |
|||
Name: 6.xml |
|||
|
|||
NORMATIVE INFORMATION |
|||
|
|||
Information about this file can be found in the latest revision of |
|||
|
|||
OMA-TS-LightweightM2M-V1_0_2 |
|||
OMA-TS-LightweightM2M_Core-V1_1_1 |
|||
|
|||
This is available at http://www.openmobilealliance.org/release/LightweightM2M/ |
|||
|
|||
Send comments to https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues |
|||
|
|||
LEGAL DISCLAIMER |
|||
|
|||
Copyright 2020 Open Mobile Alliance. |
|||
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
1. Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
2. Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
3. Neither the name of the copyright holder nor the names of its |
|||
contributors may be used to endorse or promote products derived |
|||
from this software without specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|||
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|||
POSSIBILITY OF SUCH DAMAGE. |
|||
|
|||
The above license is used as a license under copyright only. Please |
|||
reference the OMA IPR Policy for patent licensing terms: |
|||
https://www.omaspecworks.org/about/intellectual-property-rights/ |
|||
|
|||
--> |
|||
|
|||
<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.openmobilealliance.org/tech/profiles/LWM2M.xsd"> |
|||
<Object ObjectType="MODefinition"> |
|||
<Name>Location</Name> |
|||
<Description1><![CDATA[This LwM2M Object provides a range of location telemetry related information which can be queried by the LwM2M Server.]]></Description1> |
|||
<ObjectID>6</ObjectID> |
|||
<ObjectURN>urn:oma:lwm2m:oma:6</ObjectURN> |
|||
<LWM2MVersion>1.0</LWM2MVersion> |
|||
<ObjectVersion>1.0</ObjectVersion> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Resources> |
|||
<Item ID="0"> |
|||
<Name>Latitude</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Mandatory</Mandatory> |
|||
<Type>Float</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units>lat</Units> |
|||
<Description><![CDATA[The decimal notation of latitude, e.g. -43.5723 [World Geodetic System 1984].]]></Description> |
|||
</Item> |
|||
<Item ID="1"> |
|||
<Name>Longitude</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Mandatory</Mandatory> |
|||
<Type>Float</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units>lon</Units> |
|||
<Description><![CDATA[The decimal notation of longitude, e.g. 153.21760 [World Geodetic System 1984].]]></Description> |
|||
</Item> |
|||
<Item ID="2"> |
|||
<Name>Altitude</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Float</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units>m</Units> |
|||
<Description><![CDATA[The decimal notation of altitude in meters above sea level.]]></Description> |
|||
</Item> |
|||
<Item ID="3"> |
|||
<Name>Radius</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Float</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units>m</Units> |
|||
<Description><![CDATA[The value in this resource indicates the radius of a circular area in meters. The circular area is used to describe uncertainty about a point for coordinates in a two-dimensional coordinate reference systems (CRS). The center point of a circular area is specified by using the Latitude and the Longitude Resources.]]></Description> |
|||
</Item> |
|||
<Item ID="4"> |
|||
<Name>Velocity</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Opaque</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description><![CDATA[The velocity of the LwM2M Client, as defined in [3GPP-TS_23.032].]]></Description> |
|||
</Item> |
|||
<Item ID="5"> |
|||
<Name>Timestamp</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Mandatory</Mandatory> |
|||
<Type>Time</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description><![CDATA[The timestamp of when the location measurement was performed.]]></Description> |
|||
</Item> |
|||
<Item ID="6"><Name>Speed</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>Float</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units>m/s</Units> |
|||
<Description><![CDATA[Speed is the time rate of change in position of a LwM2M Client without regard for direction: the scalar component of velocity.]]></Description> |
|||
</Item></Resources> |
|||
<Description2></Description2> |
|||
</Object> |
|||
</LWM2M> |
|||
Loading…
Reference in new issue