Using Grails to Power your Electric Car

44
Using Grails to power your Electric Car

Transcript of Using Grails to Power your Electric Car

Using Grails to power your Electric Car

Small Intro

● Marco Pas (1973)

› Education in Chemistry and then moved to Transportation & Logistics to finally IT.. what is next?

● Software Engineer

● Burgundian lifestyleWikipedia tells us :

'enjoyment of life, good food, and extravagant spectacle'

Agenda

● What the heck am I trying to solve!!

● How on earth did we solve it?

● What are our plans for the future?

Disclaimer....

Electric Cars vs Electric Vehicles

Convention Fueling != Electric Vehicle Charging

● EV Charging Challenges:

› Impossible to store & forward electricity

› Charge often (Limited Range)

› Time to charge (from minutes to hours)

› Compatibility (plugs, charging cables)

Not kidding... !!Range Anxiety Reducer :)

Public EV Chargers in numbers

● 2011: EU 12.000● 2020: EU 660.000

2011 2020

Denmark 280 5.000

Germany 1.900 150.000

Italy 1.300 125.000

Netherlands 1.700 32.000

United Kingdom 703 122.000

EV Chargers in the wild

Charging Process

Back OfficeValidation / Verification

● Is the card valid?● Electricity pricing?● How much can I charge?● Who is the customer?● Did you make a reservation?● ….

Stakeholders involved in EV Charging

How to manage all those

(different kind of)

Chargers?Stakeholders?

Processes?

Requirements

● Implement a platform that enables EV Infra management:

› To monitor a Charge Point (CP) network

› Supports remote management

› Track charging sessions● Including charge authorization &

transaction storage

› Multi-tenancy

› 3rd party integration using Web Services (SOAP / REST)

Schematic overview of a Charge Point

Open Charge Point Protocol (OCPP)

● Open protocol between charging stations and a managing central system aka back office

› Asynchronous

› Based on SOAP (v1.2)

› Working group with support from all manufacturers!

Agenda

● What the heck am I trying to solve!!

● How on earth did we solve it?

● What are our plans for the future?

Release 1.0

!

! SOAP ~ Contract First with Grails was not that easy, so we moved to a pure JAVA/Spring OCPP application

Jackson JSON Mapper

public class JsonMapper extends ObjectMapper {

public JsonMapper() { super();

/** * De-Serialization options JSON -> OBJECT */ // - ignore unknown fields - otherwise the construction of the object will fail! this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

// - make it possible to also construct empty objects this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

/** * Serialization options OBJECT -> JSON */ // properties with non-null values are to be included in the resulting JSON this.setSerializationInclusion(JsonInclude.Include.NON_NULL); }}

Jackson JSON Mapper

● JSON → Object

● Object → JSON

resources.groovy

beans = {

// a preconfigured Jackson JSON mapper with defaults jsonMapper(JsonMapper){}}

def jsonContent = “{'name': 'John Doe'}”

Person person = jsonMapper.readValue(jsonContent, Person.class)

Person person = new Person(name: 'John Doe')

def jsonContent = jsonMapper.valueToTree(person)

Theming support

● Login-Logout & user configurable themes● Using Spring Security Core Plugin

Config.groovy

// make sure we can act on security eventsgrails.plugins.springsecurity.useSecurityEventListener = true

// executed when a user succesfully authenticates into the applicationgrails.plugins.springsecurity.onInteractiveAuthenticationSuccessEvent = { e, appCtx → // .. code intentionally emitted .. // session.theme = “MyNameOfTheTheme”}

page.gsp

<g:if test="${session?.theme == null}"> <% session.theme="${grailsApplication.config.default.theme}"%></g:if><r:require module="${session?.theme}"/><g:external uri="/css/themes/${session?.theme - "theme_"}/images/favicon.ico"/>

Chargepoint Overview

Pool Overview

Nokia Maps – Heat Map

Technical Debt

● Client was very happy but.... shame on us:

› Tightly coupled

› Poor test coverage

› Spring project & Grails project

› Adding functional value is just to much fun!

● But... ready for round 2..

› Thanks to Grails we could refactor with great ease and speed!

Release 2.0

● Guidelines:

› Focus on creating a modular platform

› Test Driven Development

› Use Grails for everything!!

› Minimize the use of plugins!!!

Grails CXF Plugin

● WSDL

› Contract First & Code First

● Wire Interceptors

› Logging, Security

● Support for versioning@GrailsCxfEndpoint(address='/myCustomSoapService/v2/')

Annotated example

@GrailsCxfEndpoint( address='/centralsystem/ocpp/v1/5', wsdl = 'wsdl/ocpp_15_centralsystem.wsdl', expose = EndpointType.JAX_WS_WSDL, soap12 = true, inInterceptors = ["logSoapInboundInterceptor", "setReplyToSOAPHeaderInInterceptor"], outInterceptors = ["logSoapOutboundInterceptor"])

@WebService( name = "CentralSystemService", targetNamespace = "urn://Ocpp/Cs/2012/06/", serviceName = "CentralSystemService", portName = "CentralSystemServiceSoap12")

@GZIPclass CentralSystemOcpp15Service implements CentralSystemService {

// ... code intentionally omitted// ... contains the methods that needs to be implemented due to the 'implements'

}

Demo

● Create a contract first webservice using Grails CXF plugin

› Source WSDL: CustomerService.wsdl

› Steps:● Create grails project● Install CXF plugin● Use WSDL2JAVA to generate web service implementation● Create Grails service that implements the web service interface● Test using SOAPUI

AMQP - Advanced Message Queuing Protocol

● Asynchronous and synchronous message exchange

› Enables modular platform architecture

RabbitMQ – an AMQP implementation

● Grails RabbitMQ Plugin

› High-level abstraction for sending and receiving messages

› Fallback to Spring Template

class MessageReceiveService {

static rabbitQueue = 'helloQ'

void handleMessage(message) { // handle message… }

}

class MessageSendController {

def sendMessage = { rabbitSend 'helloQ', 'Hello!' }

}

RabbitMQ Synchronous

class MessageSendController {

def rabbitTemplate // use the Spring rabbitTemplate directly

def sendMessage = { def response = rabbitTemplate.convertSendAndReceive 'helloQ', 'Hello World' println response }

}

class MessageReceiveService {

static rabbitQueue = [queues: 'helloQ', messageConverterBean: '']

void handleMessage(Message message) { // determine the reply queue def returnQueue = message.messageProperties.replyTo

// return the response to temporary return queue.. rabbitSend returnQueue, 'Hello there' }

}

Demo

● Send and Consume a message via RabbitMQ

› Steps:● Install RabbitMQ plugin● Configure Grails app to use RabbitMQ● Create code to publish and consume a message

Testing

● Functional & Unit Testing

› Build-Test-Data

› Spock

● Load & Performane Testing

› BadBoy / Apache JMeter

Some stuff we have used

● Grails Plugins

› Spring Security

› Export

› RabbitMQ

› CXF

› Fixture

› Spock

› Build-Test-Data

● Non-Plugins

› Twitter BootStrap

› Jackson JSON

Agenda

● What the heck am I trying to solve!!

● How on earth did we solve it?

● What are our plans for the future?

Release 3.0

● Additional protocol implementation in JSON

› Eliminating the verbosity of SOAP!

› To ([<MessageTypeId>, “<UniqueId>”, “<Action>”, {<Payload>}])

<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:cp="urn://Ocpp/Cp/2012/06/" xmlns:cs="urn://Ocpp/Cs/2012/06/" xmlns:imp="urn://iMOVE/Cp/2011/09/" xmlns:ims="urn://iMOVE/Cs/2011/09/"> <SOAP-ENV:Header> <wsa5:MessageID>940</wsa5:MessageID> <wsa5:From> <wsa5:Address>http://127.0.0.1:1234</wsa5:Address> </wsa5:From> <wsa5:ReplyTo SOAP-ENV:mustUnderstand="true"> <wsa5:Address>http://127.0.0.1:1234</wsa5:Address> </wsa5:ReplyTo> <wsa5:To SOAP-ENV:mustUnderstand="true">http://www.starwarsrules.com/services/centralsystem/ocpp/v1/5</wsa5:To> <wsa5:Action SOAP-ENV:mustUnderstand="true">/Heartbeat</wsa5:Action> <cs:chargeBoxIdentity SOAP-ENV:mustUnderstand="true">CHARGER_001_1234</cs:chargeBoxIdentity> </SOAP-ENV:Header> <SOAP-ENV:Body> <cs:heartbeatRequest /> </SOAP-ENV:Body></SOAP-ENV:Envelope>

[2, “19223201”, “HeartBeat”, {“”}]

<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:cp="urn://Ocpp/Cp/2012/06/" xmlns:cs="urn://Ocpp/Cs/2012/06/" xmlns:imp="urn://iMOVE/Cp/2011/09/" xmlns:ims="urn://iMOVE/Cs/2011/09/"> <SOAP-ENV:Header> <wsa5:MessageID>940</wsa5:MessageID> <wsa5:From> <wsa5:Address>http://127.0.0.1:1234</wsa5:Address> </wsa5:From> <wsa5:ReplyTo SOAP-ENV:mustUnderstand="true"> <wsa5:Address>http://127.0.0.1:1234</wsa5:Address> </wsa5:ReplyTo> <wsa5:To SOAP-ENV:mustUnderstand="true">http://www.starwarsrules.com/services/centralsystem/ocpp/v1/5</wsa5:To> <wsa5:Action SOAP-ENV:mustUnderstand="true">/Heartbeat</wsa5:Action> <cs:chargeBoxIdentity SOAP-ENV:mustUnderstand="true">CHARGER_001_1234</cs:chargeBoxIdentity> </SOAP-ENV:Header> <SOAP-ENV:Body> <cs:heartbeatRequest /> </SOAP-ENV:Body></SOAP-ENV:Envelope>

Release 3.0

● Using WebSockets for full-duplex communication

WebSockets

● Defines an API establishing a "socket" connections between a client and a server

› Providing full-duplex communicationchannel over a single TCP connection

› HTTP upgrade by Protocol Negotiation

› Firewall friendly! (port 80)

› No reconnect handling or guaranteed message delivery

WebSocket handshake

● Client Request

● Server Response

GET /mychat HTTP/1.1Host: server.example.comUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==Sec-WebSocket-Protocol: chatSec-WebSocket-Version: 13Origin: http://example.com

HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=Sec-WebSocket-Protocol: chat

WebSocket API Hooks

Vertx.io

● Asynchronous Application Development

› Polyglot, Simplicity, Scalability, Concurrency

› Distributed Event Bus

› WebSocket support

● VerticlesServer.groovy

vertx.createHttpServer().requestHandler { req -> req.response.headers().put("Content-Type", "text/html; charset-UTF-8"); req.response.end("<html><body><h1>Hello from vert.x!</h1></body></html>");}.listen(8080)

vertx run Server.groovy

vertx run Server.groovy -instances 4

Thank you!!

http://twitter.com/marcopas

[email protected]

http://www.linkedin.com/in/marcopas