org.openmuc.jdlms.LogicalDevice Maven / Gradle / Ivy
/**
* Copyright 2012-18 Fraunhofer ISE
*
* This file is part of jDLMS.
* For more information visit http://www.openmuc.org
*
* jDLMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* jDLMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with jDLMS. If not, see .
*/
package org.openmuc.jdlms;
import static org.openmuc.jdlms.ConformanceSetting.ACTION;
import static org.openmuc.jdlms.ConformanceSetting.BLOCK_TRANSFER_WITH_GET_OR_READ;
import static org.openmuc.jdlms.ConformanceSetting.GET;
import static org.openmuc.jdlms.ConformanceSetting.MULTIPLE_REFERENCES;
import static org.openmuc.jdlms.ConformanceSetting.PARAMETERIZED_ACCESS;
import static org.openmuc.jdlms.ConformanceSetting.READ;
import static org.openmuc.jdlms.ConformanceSetting.SELECTIVE_ACCESS;
import static org.openmuc.jdlms.ConformanceSetting.SET;
import static org.openmuc.jdlms.ConformanceSetting.WRITE;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This class represents a logical device in the physical server/meter.
*/
public class LogicalDevice {
private static final int LD_NAME_MAX_LENGTH = 16;
private final int logicalDeviceId;
private final List cosemObjects;
private final String logicalDeviceName;
private final Map restrictions;
private Set conformance;
private final String manufacturerId;
@SuppressWarnings("unused")
private final long deviceId;
private byte[] systemTitle;
private byte[] masterKey;
/**
* Creates a new Logical Device.
*
* @param logicalDeviceId
* logical device id. Id to identify the logical device. Integer greater than 0.
*
* @param logicalDeviceName
* The logical device name is defined as an octet-string of up to 16 octets/characters. The first three
* octets/characters shall carry the manufacturer identifier. The manufacturer shall ensure that the
* logical device name, starting with the three octets/characters identifying the manufacturer and
* followed by up to 13 octets/characters, is unique.
*
* @param manufacturerId
* unique String ID of three characters. See:
* DLMS UA FLAG Manufacturers ID
*
* @param deviceId
* 5 byte device ID.
*
* @throws IllegalArgumentException
* if a parameter does not fulfill its requirements.
*/
public LogicalDevice(int logicalDeviceId, String logicalDeviceName, String manufacturerId, long deviceId) {
this.manufacturerId = manufacturerId;
this.deviceId = deviceId;
if (logicalDeviceName.length() > LD_NAME_MAX_LENGTH) {
String message = MessageFormat.format("Logical device name length is greater than {0}.",
LD_NAME_MAX_LENGTH);
throw new IllegalArgumentException(message);
}
if (logicalDeviceId < 1) {
String message = "Logical Device ID must be greater than one.";
throw new IllegalArgumentException(message);
}
this.logicalDeviceId = logicalDeviceId;
this.logicalDeviceName = logicalDeviceName;
this.cosemObjects = new LinkedList<>();
this.restrictions = new HashMap<>();
setSystemTitle(manufacturerId, deviceId);
setConformance(GET, SET, ACTION, BLOCK_TRANSFER_WITH_GET_OR_READ, MULTIPLE_REFERENCES, READ, WRITE,
SELECTIVE_ACCESS, PARAMETERIZED_ACCESS);
}
private void setSystemTitle(String manufacturerId, long deviceId) {
final int manIdLength = 3;
if (manufacturerId.length() != manIdLength) {
throw new IllegalArgumentException("Manufacterer ID must be three charactes long.");
}
this.systemTitle = new byte[8];
byte[] manufacturerIdBytes = manufacturerId.getBytes(StandardCharsets.US_ASCII);
for (int i = 0; i < manIdLength; i++) {
this.systemTitle[i] = manufacturerIdBytes[i];
}
for (int i = 0; i < 5; i++) {
int shift = (4 - i) * 8;
long v = deviceId >> shift;
this.systemTitle[i + manIdLength] = (byte) (0xffL & v);
}
}
public String getLogicalDeviceName() {
return logicalDeviceName;
}
public String getManufacturerId() {
return this.manufacturerId;
}
public int getLogicalDeviceId() {
return logicalDeviceId;
}
public byte[] getMasterKey() {
return masterKey;
}
/**
* Sets the master key.
*
*
* NOTE: The master key must be 128 bits (16 byte) long.
*
*
* @param masterKey
* the master key.
* @return the logical device.
*
* @throws IllegalArgumentException
* if the key length is not supported.
*/
public LogicalDevice setMasterKey(byte[] masterKey) {
int numOfBits = masterKey.length << 3;
if (numOfBits != 128) {
throw new IllegalArgumentException("Key length not 128 bits.");
}
this.masterKey = masterKey;
return this;
}
/**
* Adds a restriction to the Logical Device. If there's no restriction set up, the server allows connections from
* all client IDs.
*
*
* NOTE: The master key must be set if a key is used in the authentication.
*
*
* @param clientId
* the client ID. Value greater than zero.
* @param securitySuite
* a new restriction/security suite for the logical device.
*
* @return true
if the clientId was not set so far, false
otherwise.
*
* @throws IllegalArgumentException
* if the arguments don't fulfill their requirements.
*
* @see #setMasterKey(byte[])
*/
public boolean addRestriction(int clientId, SecuritySuite securitySuite) {
if (clientId < 1) {
throw new IllegalArgumentException("Client ID must be grater than zero.");
}
if (securitySuite == null) {
throw new IllegalArgumentException("Authentication object must not be null.");
}
return this.restrictions.put(clientId, securitySuite) != null;
}
/**
* Register a new COSEM class. It's not allowed to register classes with identical instance IDs (OBIS code). The
* class must be annotated with CosemClass.
*
* @param cosemObject
* a class annotated with {@link CosemClass}.
* @return the LogicalDevice instance.
*
* @see CosemClass
*/
public LogicalDevice registerCosemObject(CosemInterfaceObject... cosemObject) {
return registerCosemObject(Arrays.asList(cosemObject));
}
/**
* Register a new COSEM class. It's not allowed to register classes with identical instance IDs (OBIS code). The
* class must be annotated with CosemClass.
*
* @param cosemObject
* a class annotated with {@link CosemClass}.
* @return the LogicalDevice instance.
*/
public LogicalDevice registerCosemObject(List cosemObject) {
this.cosemObjects.addAll(cosemObject);
return this;
}
public Map getRestrictions() {
return this.restrictions;
}
List getCosemObjects() {
return this.cosemObjects;
}
public Set getConformance() {
return conformance;
}
public byte[] getSystemTitle() {
return systemTitle;
}
/**
* Sets the logical device conformance. No conformance setting is set, the logical device accepts all service
* requests.
*
* @param conformanceSetting
* set the conformance settings.
* @return the LogicalDevice instance.
*/
public LogicalDevice setConformance(ConformanceSetting... conformanceSetting) {
ConformanceSetting[] cs = conformanceSetting;
if (conformanceSetting.length == 0) {
cs = ConformanceSetting.values();
}
this.conformance = new HashSet<>(Arrays.asList(cs));
return this;
}
}