Skip to content

Commit

Permalink
deduplicate Ocpp20JsonClient and Ocpp1XJsonClient
Browse files Browse the repository at this point in the history
  • Loading branch information
Reinier Lamers committed Oct 13, 2018
1 parent d28fe9d commit abdb496
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ object JsonClientTestApp extends App {
*/
ocppJsonClient.onClose.foreach(_ => println("OCPP connection closed"))

println(s"Connected using OCPP version ${ocppJsonClient.connection.ocppVersion}")
println(s"Connected using OCPP version ${ocppJsonClient.ocppVersion}")

/*
* Now let's send some OCPP requests to that Central System! Just like a real
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import scala.util.{Try, Success, Failure}
import messages.ReqRes
import messages.v1x._
import json.v1x.{JsonOperation, JsonOperations}
import org.slf4j.LoggerFactory

/**
* The OCPP layer connection component for OCPP-J 1.X, i.e. OCPP 1.5 and 1.6
Expand Down Expand Up @@ -44,8 +43,6 @@ trait Ocpp1XConnectionComponent[
protected[this] val ourOperations: JsonOperations[INREQBOUND, OUTRESBOUND, INREQRES, V]
protected[this] val theirOperations: JsonOperations[OUTREQBOUND, INRESBOUND, OUTREQRES, V]

private val logger = LoggerFactory.getLogger(Ocpp1XConnection.this.getClass)

def onSrpcCall(req: SrpcCall): Future[SrpcResponse] = {
import ourOperations._

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import scala.language.higherKinds
import messages.v20._
import json.v20._
import org.json4s.JValue
import org.slf4j.{Logger, LoggerFactory}

/** One roles of the OCPP 2.0 communication protocol: Charging Station (CS) or
* Charging Station Management System (CSMS)
Expand All @@ -30,8 +29,6 @@ trait Ocpp20ConnectionComponent[

implicit val executionContext: ExecutionContext

private val logger: Logger = LoggerFactory.getLogger(this.getClass)

trait Ocpp20Connection extends BaseOcppConnection {

def incomingProcedures: Ocpp20Procedures[INREQBOUND, OUTRESBOUND, INREQRES]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ import scala.concurrent.{ExecutionContext, Future}
*/
abstract class CakeBasedOcppClientEndpoint[
VFam <: VersionFamily,
INREQBOUND <: Request,
OUTRESBOUND <: Response,
INREQRES[_ <: INREQBOUND, _ <: OUTRESBOUND] <: ReqRes[_, _],
OUTREQBOUND <: Request,
INRESBOUND <: Response,
OUTREQRES[_ <: OUTREQBOUND, _ <: INRESBOUND] <: ReqRes[_, _]](
implicit val csMessageTypeForVersionFamily: CsMessageTypesForVersionFamily[VFam, INREQBOUND, OUTRESBOUND, INREQRES],
implicit val csmsMessageTypeForVersionFamily: CsmsMessageTypesForVersionFamily[VFam, OUTREQBOUND, INRESBOUND, OUTREQRES]
OUTREQRES[_ <: OUTREQBOUND, _ <: INRESBOUND] <: ReqRes[_, _],
INREQBOUND <: Request,
OUTRESBOUND <: Response,
INREQRES[_ <: INREQBOUND, _ <: OUTRESBOUND] <: ReqRes[_, _]
](
implicit val csmsMessageTypeForVersionFamily: CsmsMessageTypesForVersionFamily[VFam, OUTREQBOUND, INRESBOUND, OUTREQRES],
val csMessageTypeForVersionFamily: CsMessageTypesForVersionFamily[VFam, INREQBOUND, OUTRESBOUND, INREQRES]
) extends OutgoingOcppEndpoint[OUTREQBOUND, INRESBOUND, OUTREQRES] {

def send[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ package json.api
package client

import java.net.URI

import javax.net.ssl.SSLContext

import scala.language.higherKinds
import scala.concurrent.ExecutionContext
import VersionFamily.{CsMessageTypesForVersionFamily, CsmsMessageTypesForVersionFamily}
import messages.{ReqRes, Request, Response}
import messages.v1x.{CentralSystemReq, CentralSystemReqRes, CentralSystemRes, ChargePointReq, ChargePointReqRes, ChargePointRes}
import messages.v20._
import OcppJsonClient.VersionNotSupported

/**
* An OCPP-J client implemented using Java-WebSocket.
Expand All @@ -20,72 +19,54 @@ import messages.v20._
* intended for use in charge point simulators (or charge point firmwares, if
* there is someone writing those in Scala :-)).
*
* For a complete description of the interface of this object, see
* [[com.thenewmotion.ocpp.json.api.client.CakeBasedOcppClientEndpoint]].
*
* @param chargerId The charge point identity of the charge point for which you
* want to set up a connection
* @param centralSystemUri The endpoint URI of the Central System to connect to.
* @param versions A list of requested OCPP versions in order of preference e.g:
* Seq(Version.V16, Version.V15)
* @param authPassword The Basic Auth password to use, hex-encoded
*/
// TODO actually use this class and shre code, e.g. WebSocket init
abstract class OcppJsonClient[
VFam <: VersionFamily,
INREQBOUND <: Request,
OUTRESBOUND <: Response,
INREQRES[_ <: INREQBOUND, _ <: OUTRESBOUND] <: ReqRes[_, _],
OUTREQBOUND <: Request,
INRESBOUND <: Response,
OUTREQRES[_ <: OUTREQBOUND, _ <: INRESBOUND] <: ReqRes[_, _]
] private (
OUTREQRES[_ <: OUTREQBOUND, _ <: INRESBOUND] <: ReqRes[_, _],
INREQBOUND <: Request,
OUTRESBOUND <: Response,
INREQRES[_ <: INREQBOUND, _ <: OUTRESBOUND] <: ReqRes[_, _]
] private[client] (
chargerId: String,
centralSystemUri: URI,
versions: Seq[Version],
authPassword: Option[String] = None
)(implicit val ec: ExecutionContext,
val csMessages: CsMessageTypesForVersionFamily[VFam, INREQBOUND, OUTRESBOUND, INREQRES],
val csmsMessages: CsmsMessageTypesForVersionFamily[VFam, OUTREQBOUND, INRESBOUND, OUTREQRES],
val csMessages: CsMessageTypesForVersionFamily[VFam, INREQBOUND, OUTRESBOUND, INREQRES],
sslContext: SSLContext = SSLContext.getDefault
) extends CakeBasedOcppClientEndpoint[
VFam,
INREQBOUND,
OUTRESBOUND,
INREQRES,
OUTREQBOUND,
INRESBOUND,
OUTREQRES
]

abstract class Ocpp1XJsonClient private[client](
chargerId: String,
centralSystemUri: URI,
versions: Seq[Version],
authPassword: Option[String] = None
)(implicit val ec: ExecutionContext,
sslContext: SSLContext = SSLContext.getDefault
) extends CakeBasedOcppClientEndpoint[
VersionFamily.V1X.type,
ChargePointReq,
ChargePointRes,
ChargePointReqRes,
CentralSystemReq,
CentralSystemRes,
CentralSystemReqRes
]
{
OUTREQRES,
INREQBOUND,
OUTRESBOUND,
INREQRES
] {

val connection: ConnectionCake = new ConnectionCake
with ChargePointOcpp1XConnectionComponent
protected abstract class BaseConnectionCake(versionsToRequest: Seq[Version])
extends ConnectionCake
with DefaultSrpcComponent
with SimpleClientWebSocketComponent {

import OcppJsonClient._
self: OcppConnectionComponent[OUTREQBOUND, INRESBOUND, OUTREQRES, INREQBOUND, OUTRESBOUND, INREQRES] =>

import SimpleClientWebSocketComponent._

val webSocketConnection = new SimpleClientWebSocketConnection(
chargerId,
centralSystemUri,
authPassword,
requestedSubProtocols = versions.map { version =>
requestedSubProtocols = versionsToRequest.map { version =>
wsSubProtocolForOcppVersion.getOrElse(
version,
throw VersionNotSupported(
Expand All @@ -96,60 +77,104 @@ abstract class Ocpp1XJsonClient private[client](
}
)

val ocppVersion = ocppVersionForWsSubProtocol.getOrElse(
val negotiatedOcppVersion = ocppVersionForWsSubProtocol.getOrElse(
webSocketConnection.subProtocol,
throw new RuntimeException(s"Unknown protocol ${webSocketConnection.subProtocol} in use for connection")
)

val srpcConnection = new DefaultSrpcConnection
val ocppConnection = defaultChargePointOcppConnection
}

protected val connection: BaseConnectionCake

/**
* @return the OCPP version that this client object is using
*/
def ocppVersion: Version = connection.ocppVersion
}

abstract class Ocpp20JsonClient private[client] (
/** An OCPP-J client class for versions 1.5 and 1.6.
*
* @param chargerId The charge point identity of the charge point for which you
* want to set up a connection
* @param centralSystemUri The endpoint URI of the Central System to connect to.
* @param versions A list of requested OCPP versions in order of preference e.g:
* Seq(Version.V16, Version.V15)
* @param authPassword The Basic Auth password to use, hex-encoded
*/
abstract class Ocpp1XJsonClient private[client] (
chargerId: String,
centralSystemUri: URI,
versions: Seq[Version],
authPassword: Option[String] = None
)(implicit val ec: ExecutionContext,
)(implicit ec: ExecutionContext,
sslContext: SSLContext = SSLContext.getDefault
) extends CakeBasedOcppClientEndpoint[
VersionFamily.V20.type,
CsRequest,
CsResponse,
CsReqRes,
CsmsRequest,
CsmsResponse,
CsmsReqRes
]
) extends OcppJsonClient[
VersionFamily.V1X.type,
CentralSystemReq,
CentralSystemRes,
CentralSystemReqRes,
ChargePointReq,
ChargePointRes,
ChargePointReqRes
](
chargerId,
centralSystemUri,
authPassword
)
{

val connection: ConnectionCake = new ConnectionCake
with CsOcpp20ConnectionComponent
with DefaultSrpcComponent
with SimpleClientWebSocketComponent {
override protected val connection: BaseConnectionCake =
new BaseConnectionCake(versions) with ChargePointOcpp1XConnectionComponent {

import SimpleClientWebSocketComponent._
val ocppVersion: Version = negotiatedOcppVersion

val subprotocolToRequest = wsSubProtocolForOcppVersion(Version.V20)

val webSocketConnection = new SimpleClientWebSocketConnection(
chargerId,
centralSystemUri,
authPassword,
requestedSubProtocols = Seq(subprotocolToRequest)
)
val ocppConnection = defaultChargePointOcppConnection
}
}

if (webSocketConnection.subProtocol != subprotocolToRequest) {
throw new RuntimeException(
s"Server using protocol ${webSocketConnection.subProtocol} instead of $subprotocolToRequest"
)
/** An OCPP-J client class for version 2.0.
*
* @param chargerId The charge point identity of the charge point for which you
* want to set up a connection
* @param centralSystemUri The endpoint URI of the Central System to connect to.
* @param authPassword The Basic Auth password to use, hex-encoded
*/
abstract class Ocpp20JsonClient private[client] (
chargerId: String,
centralSystemUri: URI,
authPassword: Option[String] = None
)(implicit ec: ExecutionContext,
sslContext: SSLContext = SSLContext.getDefault
) extends OcppJsonClient[
VersionFamily.V20.type,
CsmsRequest,
CsmsResponse,
CsmsReqRes,
CsRequest,
CsResponse,
CsReqRes
](
chargerId,
centralSystemUri,
authPassword
) {

override val connection: BaseConnectionCake =
new BaseConnectionCake(Seq(Version.V20)) with CsOcpp20ConnectionComponent {

if (negotiatedOcppVersion != Version.V20) {
throw new RuntimeException(
s"Server using protocol ${webSocketConnection.subProtocol} instead of ocpp2.0"
)
}
}

val srpcConnection = new DefaultSrpcConnection
}
}

object OcppJsonClient {
// TODO: a factory method that uses the version negotiation to give 2.0 if
// supported and otherwise 1.x?

/**
* The factory method to create an OcppJsonClient for OCPP 1.2, 1.5 and/or
* 1.6.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,22 @@ class OcppJsonClientSpec extends Specification {
"when requesting ocpp1.6" in new TestScope {
val requesting = List(Version.V16)
server.when(request().withMethod("GET").withPath(s"$path/$chargerId")).respond(version15And16Responder)
createOcppJsonClient(requesting).connection.ocppVersion must beEqualTo(Version.V16)
createOcppJsonClient(requesting).ocppVersion must beEqualTo(Version.V16)
}
"when requesting ocpp1.5, ocpp1.6" in new TestScope {
val requesting = List(Version.V15, Version.V16)
server.when(request().withMethod("GET").withPath(s"$path/$chargerId")).respond(version15And16Responder)
createOcppJsonClient(requesting).connection.ocppVersion must beEqualTo(Version.V15)
createOcppJsonClient(requesting).ocppVersion must beEqualTo(Version.V15)
}
"when requesting ocpp1.6, ocpp1.5" in new TestScope {
val requesting = List(Version.V16, Version.V15)
server.when(request().withMethod("GET").withPath(s"$path/$chargerId")).respond(version15And16Responder)
createOcppJsonClient(requesting).connection.ocppVersion must beEqualTo(Version.V16)
createOcppJsonClient(requesting).ocppVersion must beEqualTo(Version.V16)
}
"when requesting ocpp1.6, ocpp1.5 and server supports 1.5 only" in new TestScope {
val requesting = List(Version.V16, Version.V15)
server.when(request().withMethod("GET").withPath(s"$path/$chargerId")).respond(version15Responder)
createOcppJsonClient(requesting).connection.ocppVersion must beEqualTo(Version.V15)
createOcppJsonClient(requesting).ocppVersion must beEqualTo(Version.V15)
}
"when requesting ocpp1.6 and server supports 1.5 only" in new TestScope {
val requesting = List(Version.V16)
Expand Down

0 comments on commit abdb496

Please sign in to comment.