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);
jSPINE then handles all messages, parses notifications and manages acknowledgements.