Code Monkey home page Code Monkey logo

maeve-csms's Introduction

Manager Gateway

MaEVe

MaEVe is an EV charge station management system (CSMS). It began life as a simple proof of concept for implementing ISO-15118-2 Plug and Charge (PnC) functionality and remains a work in progress. It is hoped that over time it will become more complete, but already provides a useful basis for experimentation.

The system currently integrates with Hubject for PnC functionality.

Table of Contents

Documentation

MaEVe is implemented in Go 1.20. Learn more about MaEVe and its existing components through this High-level design document.

Pre-requisites

MaEVe runs in a set of Docker containers. This means you need to have docker, docker-compose and a docker daemon (e.g. docker desktop, colima or rancher) installed and running. Scripts that fetch various tokens use jq. Make sure you have it installed.

Getting started

To get the system up and running:

  1. (cd config/certificates && make)
  2. Run the ./scripts/run.sh script

Charge stations can connect to the CSMS using:

  • ws://localhost/ws/<cs-id>
  • wss://localhost/ws/<cs-id>

If the charge station is also running in a Docker container then the charge station docker container can connect to the maeve-csms network and the charge station can connect to the CSMS using:

  • ws://gateway:9310/ws/<cs-id>
  • wss://gateway:9311/ws/<cs-id>

Charge stations can use either OCPP 1.6j or OCPP 2.0.1.

For TLS, the charge station should use a certificate provisioned using the Hubject CPO EST service.

A charge station must first be registered with the CSMS before it can be used. This can be done using the manager API. e.g. for TLS with client certificate, use:

$ curl http://localhost:9410/api/v0/cs/<cs-id> -H 'content-type: application/json' -d '{"securityProfile":2}'

Tokens, which identify a payment method for a non-contract charge, must also be registered with the CSMS before they can be used. This can also be done using the manager API. e.g.:

$ curl http://localhost:9410/api/v0/token -H 'content-type: application/json' -d '{
  "countryCode": "GB",
  "partyId": "TWK",
  "type": "RFID",
  "uid": "DEADBEEF",
  "contractId": "GBTWK012345678V",
  "issuer": "Thoughtworks",
  "valid": true,
  "cacheMode": "ALWAYS"
}'

Troubleshooting

Docker compose doesn't always rebuild the docker images which can cause all kinds of errors. If in doubt, force a rebuild by docker-compose build before launching containers.

java.io.IOException: keystore password was incorrect This error results from incompatibility between java version and openssl; try upgrading your java version.

Configuration

The gateway is configured through command-line flags. The available flags can be viewed using the -h flag.

The manager is configured through a TOML configuration file. An example configuration file can be found in ./config/manager/config.toml. Details of the available configuration options can be found in ./manager/config/README.md.

Contributing

Learn more about how to contribute on this project through Contributing

License

MaEVe is Apache licensed.

maeve-csms's People

Contributors

ahl-tw avatar alessioerosferri avatar belarte-tw avatar dependabot[bot] avatar efesler avatar kaihendry avatar laurabassani avatar neinkeinkaffee avatar subnova avatar usuletw022 avatar vbdavid avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

maeve-csms's Issues

Installation problems on Debian GNU/Linux 12.5 (kvm/AMD64)

When I do the same like before, but this time within a KVM virtual machine (AMD64)...
Running the run.sh script leads to the following... how do I solve this?

~/maeve-csms/scripts# ./run.sh
WARNING: The UID variable is not set. Defaulting to a blank string.
WARNING: The GID variable is not set. Defaulting to a blank string.
Building manager
Sending build context to Docker daemon  2.715MB
Step 1/14 : FROM golang:1.20-alpine AS builder
 ---> 71719a2da3d1
Step 2/14 : RUN apk add --no-cache git openssh ca-certificates
 ---> Using cache
 ---> a57a08d5d36a
Step 3/14 : WORKDIR /src
 ---> Using cache
 ---> 74ada187e05e
Step 4/14 : ARG TARGETARCH
 ---> Using cache
 ---> fd8375d5f725
Step 5/14 : RUN if [ "$TARGETARCH" = "arm64" ]; then         TARGETARCH=aarch64 ;         fi;     wget -O /usr/bin/curl https://github.com/moparisthebest/static-curl/releases/download/v8.0.1/curl-$TARGETARCH         && chmod +x /usr/bin/curl
 ---> Running in 2d6c4270cfb8
Connecting to github.com (140.82.121.4:443)
wget: server returned error: HTTP/1.1 404 Not Found
The command '/bin/sh -c if [ "$TARGETARCH" = "arm64" ]; then         TARGETARCH=aarch64 ;         fi;     wget -O /usr/bin/curl https://github.com/moparisthebest/static-curl/releases/download/v8.0.1/curl-$TARGETARCH         && chmod +x /usr/bin/curl' returned a non-zero code: 1
ERROR: Service 'manager' failed to build : Build failed

Permission error when installing MaEve

I'm installing MaEve CSMS, my goal after installing is to connect it to Everest.

I am executing the run.sh script but I am faced with a permission error, I leave you a descriptive image below.
what is the solution to this problem?
someone to help me please.

Capture d'écran 2024-04-30 123316

Installation problems on Debian GNU/Linux 12.5 (bare metal)

Running the run.sh script leads to the following... how do I solve this?

~/maeve-csms/scripts # ./run.sh
WARNING: The UID variable is not set. Defaulting to a blank string.
WARNING: The GID variable is not set. Defaulting to a blank string.
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 704, in urlopen
    httplib_response = self._make_request(
                       ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 399, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/lib/python3.11/http/client.py", line 1282, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib/python3.11/http/client.py", line 1328, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.11/http/client.py", line 1277, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.11/http/client.py", line 1037, in _send_output
    self.send(msg)
  File "/usr/lib/python3.11/http/client.py", line 975, in send
    self.connect()
  File "/usr/lib/python3/dist-packages/docker/transport/unixconn.py", line 30, in connect
    sock.connect(self.unix_socket)
FileNotFoundError: [Errno 2] No such file or directory

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/requests/adapters.py", line 489, in send
    resp = conn.urlopen(
           ^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 788, in urlopen
    retries = retries.increment(
              ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 550, in increment
    raise six.reraise(type(error), error, _stacktrace)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/six.py", line 718, in reraise
    raise value.with_traceback(tb)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 704, in urlopen
    httplib_response = self._make_request(
                       ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 399, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/lib/python3.11/http/client.py", line 1282, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib/python3.11/http/client.py", line 1328, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.11/http/client.py", line 1277, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.11/http/client.py", line 1037, in _send_output
    self.send(msg)
  File "/usr/lib/python3.11/http/client.py", line 975, in send
    self.connect()
  File "/usr/lib/python3/dist-packages/docker/transport/unixconn.py", line 30, in connect
    sock.connect(self.unix_socket)
urllib3.exceptions.ProtocolError: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/docker/api/client.py", line 214, in _retrieve_server_version
    return self.version(api_version=False)["ApiVersion"]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/docker/api/daemon.py", line 181, in version
    return self._result(self._get(url), json=True)
                        ^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/docker/utils/decorators.py", line 46, in inner
    return f(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/docker/api/client.py", line 237, in _get
    return self.get(url, **self._set_request_timeout(kwargs))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 600, in get
    return self.request("GET", url, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 587, in request
    resp = self.send(prep, **send_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 701, in send
    r = adapter.send(request, **kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/requests/adapters.py", line 547, in send
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/bin/docker-compose", line 33, in <module>
    sys.exit(load_entry_point('docker-compose==1.29.2', 'console_scripts', 'docker-compose')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/compose/cli/main.py", line 81, in main
    command_func()
  File "/usr/lib/python3/dist-packages/compose/cli/main.py", line 200, in perform_command
    project = project_from_options('.', options)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/compose/cli/command.py", line 60, in project_from_options
    return get_project(
           ^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/compose/cli/command.py", line 152, in get_project
    client = get_client(
             ^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/compose/cli/docker_client.py", line 41, in get_client
    client = docker_client(
             ^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/compose/cli/docker_client.py", line 170, in docker_client
    client = APIClient(use_ssh_client=not use_paramiko_ssh, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/docker/api/client.py", line 197, in __init__
    self._version = self._retrieve_server_version()
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/docker/api/client.py", line 221, in _retrieve_server_version
    raise DockerException(
docker.errors.DockerException: Error while fetching server API version: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

Running within a Podman unprivileged pod

I'm trying to test maeve-csms under Podman. While I was able to start all containers, I still fail to authenticate with occp-1.6 and I'm wondering if I do not misunderstood the login process.

Under podman/pod

  • containers share a unique network addr (hostname=cscms-host=10.88.0.10)
  • containers share a unique config volume
  • this imposes minor change on config params/values

Provisioning a charger-id with manager/api works
I may provision and verify that charging station was created

curl http://csms-host:9410/api/v0/cs/Tux-Basic -H 'content-type: application/json' -d '{"securityProfile":0, "base64SHA256Password":"c25vb3B5Cg=="}}'
curl http://csms-host:9410/api/v0/cs/Tux-Basic/auth
 > {"base64SHA256Password":"c25vb3B5Cg==","securityProfile":0}

Then I try to log with ocpp-rpc client
I already use ocpp-rpc client with other server, also I known the protocol works. In fact checking here after wireshark image, we may see that the request/authen is clean. Even if the password encoding is probably wrong.

const { RPCClient } = require('ocpp-rpc');
const cli = new RPCClient({
    endpoint: 'ws://csms-host:9310/ws', // the OCPP endpoint URL
    identity: 'Tux-Basic',
    password: 'c25vb3B5Cg==',            // the OCPP identity
    protocols: ['ocpp1.6'],          // client understands ocpp1.6 subprotocol
    strictMode: true,                // enable strict validation of requests & responses
});

Question:

  • how should I encode my password. When I look to wireshark the encoding does not seems to match ?
  • would someone have a working sample with a charing-station provisioning and a wireshark dump to check what should be sent

Here after my logs & startup script

Gateway log

2023/10/12 18:12:54 ERROR GET /ws/{id} duration=445.212µs http.scheme=ws http.method=GET http.url=/ws/Tux-Basic http.route=/ws/{id} csId=Tux-Basic ocpp.security_profile=0 auth.failure_reason="invalid password" http.status_code=401

Manager log

time=2023-10-12T18:12:16.140Z level=INFO msg="GET /api/v0/cs/Tux-B/auth" remote_addr=10.88.0.10:55506 status=404 bytes=23 duration=16.174µs
time=2023-10-12T18:12:54.321Z level=INFO msg="GET /api/v0/cs/Tux-Basic/auth" remote_addr=10.88.0.10:47434 status=200 bytes=60 duration=15.514µs

My Podman startup script

#!/bin/bash

# Script to start maeve-cms inside a podman 'pod'
# author: Fulup Ar Foll (iot.bzh)
# Licence: Apache-v2

if test -z "$ROOTDIR"; then
    ROOTDIR=`dirname $0`
fi
echo "Container rootdir=$ROOTDIR"

if test -z "$PODIP"; then
    PODIP=10.88.0.10
fi
echo "Container POD IP=$PODIP"


# restart from stratch
podman pod rm -f csms-pod

# rebuild local-image
for DOCKERFILE in  $ROOTDIR/*/Dockerfile; do
   podman build -f $DOCKERFILE
   if test $? != 0; then
    echo "Fail to rebuild $DOCKERFILE"
   fi
done

#podman pod create --name=csms-pod --hostname=csms-host  -p 9410:9410 -p 9411:9411 -v $ROOTDIR/config:/config:Z   # unprivileged
podman pod create --name=csms-pod --hostname=csms-host --ip=$PODIP --network=podman  -v $ROOTDIR/config:/config:Z # privileged
podman create --pod csms-pod --name=csms_debug --interactive -u 10000:10000 alpine
podman create --pod csms-pod --name=csms_mqtt -u 10000:10000 eclipse-mosquitto:2 "/usr/sbin/mosquitto" "-c" "/config/mosquitto/mosquitto.conf"
podman create --pod csms-pod --name=csms_lb --net csms --network-alias lb envoyproxy/envoy:v1.26-latest
podman create --pod csms-pod  --name=csms_firestore google/cloud-sdk gcloud emulators firestore start --host-port=0.0.0.0:8080
podman create --pod csms-pod --name=csms_manager --requires=csms_mqtt -u 10000:10000 localhost/maeve-csms_manager serve -c /config/manager/config.toml
podman create --pod csms-pod --name=csms_gateway --requires=csms_mqtt,csms_manager localhost/maeve-csms_gateway serve --ws-addr :9310 --wss-addr :9311 --status-addr :9312 --tls-server-cert /config/certificates/csms.pem --tls-server-key /config/certificates/csms.key --tls-trust-cert /config/certificates/trust.pem --mqtt-addr mqtt://csms-host:1883 --manager-api-addr http://csms-host:9410

for CONTAINER in csms_debug csms_firestore csms_mqtt csms_manager csms_gateway; do
 echo starting: $CONTAINER
 podman start $CONTAINER
 if test $? != 0; then
   echo "fail to start container: $CONTAINER"
   exit
 fi
done

ocpp-login

Envoy Load Balancer: Connection reset by peer

Is anyone else having issues with the Envoy load balancer present in the Docker Compose deployment? I am not able to talk to the manager service's API on port 9410 (or any of the other services the load balancer is acting as a reverse proxy for) through the load balancer. The manager's API is not the problem since I can curl the API from the host if I map the manager's API port to a local port, and I can curl the API port from within the load balancer container.

I've tried debugging Envoy to see what the issue is but even with it's log level set to debug I'm not seeing anything obvious. Is there a reason why such a complex reverse proxy solution, or even a reverse proxy at all, is being used as part of the default Docker Compose config? I see the need for something like Envoy in a large-scale production deployment of MaEVe in Kubernetes for example, but not in a Docker Compose style deployment.

Understanding relation with open.plugncharge-test.hubject.com

I would be please to get some explanation on how/when 'hubject.com' enter in the process and how we may use it to test plug&charge.

Looks like manager/config.toml already have an authorization token.

  • is this token for anyone making test ?
  • should every one request a private token ? If then where ?
  • what the process to retrieve testing certificates to feed the ISO15118 ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.