Example for server and client functions
Server Functionality
With jEEBus.SPINE 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, jEEBus.SPINE executes the read
method and replies with the returned cmd
.
The deviceDiagnosisStateData
function could be represented as following in jEEBus.SPINE (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 jEEBus.SPINE 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 jEEBus.SPINE.
SPINE Device creation
To build a SPINE Device with jEEBus.SPINE the DeviceBuilder
class is used. The Device is firstly prepared by setting required information like a communication implementation – e.g. ShipCommunication using jEEBus.SHIP, 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 .setId("d:_n:EVSECC-Demo_EVSE") // should be unique and include the IANA PEN .setDeviceType(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); eb.addFeature() .setRole(RoleType.SERVER) // the Feature provides data .setType(FeatureTypeEnumType.DEVICE_CLASSIFICATION) .addFunction(new DeviceClassificationManufacturerDataFunction()) .setFeaturePermission(subsAllowed) .apply(); eb.addFeature() .setRole(RoleType.SERVER) .setType(FeatureTypeEnumType.DEVICE_DIAGNOSIS) .addFunction(new DeviceDiagnosisStateDataFunction()) .setFeaturePermission(subsAllowed) .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 jEEBus.SPINE 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);