Example for server and client functions
Server Functionality
With jSPINE the developer extends the abstract class FeatureFunction
to represent each of these functions
.
If a Use Case does not use write messages only the read
method needs to be implemented and changes in data must be notified to any subscribers. Every time a client sends a read message to this funtion, jSPINE executes the read
method and replies with the returned cmd
.
The deviceDiagnosisStateData
function could be represented as following in jSPINE (omitting import statements):
public class DeviceDiagnosisStateDataFunction extends FeatureFunction {
private DeviceDiagnosisStateDataType stateData; // holds the data
public DeviceDiagnosisStateDataFunction() {
super(FunctionEnumType.DEVICE_DIAGNOSIS_STATE_DATA.value());
// marks function as readable in DetailedDiscovery, but not as partially readable
setReadable(true, false);
}
// arguments can be ignored here
// filter would be used for partial reads
@Override
public CmdType read(FilterType filter, FeatureAddressType sourceAddress) {
CmdType replyCmd = new CmdType();
replyCmd.setDeviceDiagnosisStateData(stateData);
return replyCmd;
/* replyCmd represents this XML (with example data):
<cmd>
<deviceDiagnosisStateData>
<operatingState>failure</operatingState>
<lastErrorCode>EV exploded</lastErrorCode>
</deviceDiagnosisStateData>
</cmd>
*/
}
@Override
public SpineAcknowledgment write(CmdType cmd, FeatureAddressType sourceAddress) {
throw new UnsupportedOperationException(); // function is not writable
}
@Override
public SpineAcknowledgment call(FeatureAddressType sourceAddress) {
throw new UnsupportedOperationException(); // function is not callable
}
// simplified; this would update the stateData even if no actual change occured
public void updateStateData(DeviceDiagnosisStateDataType stateData) {
this.stateData = stateData;
// notifies any subscribers about the change
feature.notifySubscribers(FunctionEnumType.DEVICE_DIAGNOSIS_STATE_DATA, null);
}
}
Note that the UnsupportedOperationException
in the above code example is actually never thrown as jSPINE would deny write and call requests before executing the related methods. The default IDE behaviour of returning null
when overriding these methods is fine, but throwing the exception makes the behaviour more obvious and helps readability.
An instance of the FeatureFunction can then be added to a SPINE Feature when building the Feature with jSPINE.
SPINE Device creation
To build a SPINE Device with jSPINE the DeviceBuilder
class is used. The Device is firstly prepared by setting required information like a communication implementation – e.g. ShipCommunication using jSHIP, an ID (i.e. the SPINE Device address) and the Device type. Afterwards the UseCase specific data is set (UseCase metadata, UseCase specific data model).
DeviceBuilder db = Device.getBuilder();
// required information
Communication comm = new ShipCommunication(...); // constructor arguments omitted for simplicity
db.setCommunication(comm); // implementation is protocol dependent, e.g. jSHIP
db.setId("d:_n:EVSECC-Demo_EVSE"); // should be unique and include the IANA PEN
db.setType(DeviceTypeEnumType.GENERIC);
// EVSECC UseCase specific data
// UseCase interface implementation to make the UseCase discoverable via the UseCaseDiscovery
// Details omitted for simplicity (provides simple data like the UseCase name)
UseCase evsecc = new UseCase(...);
db.addUseCase(evsecc);
// bindingAllowed override omitted here as no bindings are used
FeaturePermission subsAllowed = new FeaturePermission() {
@Override
boolean subscriptionAllowed(SubscriptionRequest request) {
return true;
}
};
// following could also be put into UseCase#setup(DeviceBuilder)
EntityBuilder eb = db.addEntity().setType(EntityTypeEnumType.EVSE);
FeatureBuilder deviceClassificationFb = eb.addFeature();
deviceClassificationFb.setRole(RoleType.SERVER); // the Feature provides data
deviceClassificationFb.setType(FeatureTypeEnumType.DEVICE_CLASSIFICATION);
deviceClassificationFb.addFunction(new DeviceClassificationManufacturerDataFunction());
deviceClassificationFb.setFeaturePermission(subsAllowed);
deviceClassificationFb.apply();
FeatureBuilder deviceDiagnosisFb = eb.addFeature();
deviceDiagnosisFb.setRole(RoleType.SERVER);
deviceDiagnosisFb.setType(FeatureTypeEnumType.DEVICE_DIAGNOSIS);
deviceDiagnosisFb.addFunction(new DeviceDiagnosisStateDataFunction());
deviceDiagnosisFb.setFeaturePermission(subsAllowed);
deviceDiagnosisFb.apply();
// Finalize build
eb.applyToDevice();
db.build(); // Device connects and is reachable now
After db.build();
was executed the SPINE Device is started and listens for messages on the communication protocol. Requests are handled by jSPINE and the SPINE Device can be discovered via the DetailedDiscovery on the NodeManagement Feature.
Client Functionality
The energy manager requests data from the EVSE by sending read messages to the EVSE functions and subscribes to the EVSE Features to get notified about any runtime changes:
// cmd contains the function which shall be read on the Feature
CmdType dcReadCmd = new CmdType();
dcReadCmd.setDeviceClassificationManufacturerData(
new DeviceClassificationManufacturerData());
// parse methods extract the Function data and process it
clientFeature.requestSubscription(evseDeviceClassificationAddress,
FeatureTypeEnumType.DEVICE_CLASSIFICATION, this::parseDCUpdate);
clientFeature.requestRead(evseDeviceClassificationAddress, dcReadCmd)
.whenComplete(this::parseDCReply);
CmdType ddReadCmd = new CmdType();
ddReadCmd.setDeviceDiagnosisStateData(new DeviceDiagnosisStateData());
clientFeature.requestSubscription(evseDeviceDiagnosisAddress,
FeatureTypeEnumType.DEVICE_DIAGNOSIS, this::parseDDUpdate);
clientFeature.requestRead(evseDeviceDiagnosisAddress, ddReadCmd)
.whenComplete(this::parseDDReply);