2021년 4월 14일 수요일

Python OCPP 패키지 - Server 소스 해설

파이썬용 OCPP 패키지 서버 해설 - 원문 링크

 OCPP(Open Charge Point Protocol)은 두가지 역할 이 있다 - 중앙 서버(서버) 와 충전포인트(클라이언트). OCPP Python 패키지는 양쪽 연결을 모델링하는데 사용 가능하다.

이 문서는 중앙서버를 만드는 방법과 서버측에서 충전포인트를 모델링하는방법을 설명한다.  이 페이지 대부분의 예제는 websockets 계층 구현을위하여 websockets 라이브러리를 사용한다. 꼭 이걸 사용하지 않고, 다른 웹소켓을 적용해도 동작은 잘 될것이다.

Create a websocket server

아래 아주 간단한 예제는 접속 포트 9000을 사용하는 웹소켓 서버이고, 새로운 모든 웹소켓 연결에 대해 'Charge point connected' 를 인쇄한다.

import asyncio
import websockets


async def on_connect(websocket, path):
   await websocket.send('Connection made succesfully.')
   print(f'Charge point {path} connected')


async def main():
   server = await websockets.serve(
      on_connect,
      '0.0.0.0',
      9000,
      subprotocols=['ocpp1.6']
   )

   await server.wait_closed()


if __name__ == '__main__':
   asyncio.run(main())

설명이 필요한 두가지가 있다.

  ㅇ on_connect()에 첫번째 인자로 websockets.server() 의 핸들러가 전달된다. 이 핸들러는 모든 새 연결에서 실행된다.

핸들러는 2개의 인자를 전달 하는데, websockets.server.WebSocketServerProtocol과 요청 URI 이다.  요청 URI는 연결을 요청한 충전포인트의 식별자로 사용된다. 아래에 OCCP-J 사양의 3.1.1절을 인용한다.

  "충전포인트의 연결 URI는 충전점의 식별자가 포함되어, 중앙 서버가 어떤 충전점에 해당 websocket이 속해있는지 알게 한다."

  이 예제의 핸들러는 클라이언트에 메시지를 보내고 콘솔에 메시지를 출력한다.

  ㅇ websockets.server()의 subprotocol 인자는 OCPP 1.6을 지원하는 서버를 구성하는데 사용된다.


서버를 시작한 후, 웹소켓 대화형 클라이언트를 사용하여 클라이언트를 서버에 연결할 수 있다.

$ python -m websockets ws://localhost:9000/test_charge_point
Connected to ws://localhost:9000/test_charge_point.
< Connection made successfully.
Connection closed: code = 1000 (OK), no reason.


OCPP compliant handler 

(OCPP 준수(규정을 따르는) 핸들러)

위의 웹소켓 서버 예제는 OCPP 규정을 따르지 않기 때문에, 그닥 좋지 않다.

위의 코드에서 on_connect() 핸들러를 제거하고 다음 예제대로 바꾼다.

from datetime import datetime

from ocpp.routing import on
from ocpp.v16 import ChargePoint as cp
from ocpp.v16.enums import Action, RegistrationStatus
from ocpp.v16 import call_result


class MyChargePoint(cp):
    @on(Action.BootNotification)
    def on_boot_notitication(self, charge_point_vendor, charge_point_model, **kwargs):
        return call_result.BootNotificationPayload(
            current_time=datetime.utcnow().isoformat(),
            interval=10,
            status=RegistrationStatus.accepted
        )


async def on_connect(websocket, path):
    """ For every new charge point that connects, create a ChargePoint instance
    and start listening for messages.

    """
    charge_point_id = path.strip('/')
    cp = MyChargePoint(charge_point_id, websocket)

    await cp.start()

on_connect() 핸들러가 갱신되면, 이제 MyChargePoint 인스턴스를 생성하고 start() 코(부속)루틴을 호출한다. 

MyChargePoint는 ocpp.v16.ChargePoint의 Subclass 이다. ocpp.v16.ChargePoint는 OCPP 패키지의 핵심이다.  이 클래스는 클라이언트에서 올라오는 메시지를 올바른 핸들러(처리기)에 할당한다.  또한, 송/수신중인 모든 메시지의 유효성을 검사하고 흐름제어를 구현한다.

MyChargePoint 클래스는 'BootNotification' 요청 구현을 위해서 @On() 데코레이터를 사용한다. @On() 은 단일 인자로 문자열로된 액션의 이름을 사용한다.  비록 이 예제에서는 사용하지 않았지만, ocpp 패키지는 후처리 요청 핸들러로 사용 가능한 @after() 데코레이터도 제공한다. 

OCPP 스펙(사양)에 따르면, BootNotification 요청의 내용(페이로드)에 반드시 필요한 2가지는 'chargePointModel' 과 'chargePointVendor' 이고, 부가적으로 7가지의 옵션 인자가 있다.  핸들러(처리기)는 두개의 필수인자 charge_point_vendor 와 charge_point_model을 사용해서 이를 적용한다.  핸들러는 옵션 인자로 **kwargs 를 사용한다.

핸들러(처리기)는 ocpp.v16.call_result.BootNotificationPayload의 인스턴스를 반환한다.  이 객체는 클라이언트로 돌려보내는 응답을 만들 때 사용한다.

노트:

OCPP는 페이로드 키값에 낙타형 명명방식을 사용하는데, 파이썬은 snake_case 방식을 사용한다.  그러므로, 이 ocpp 패키지는 메시지의 모든 키를 낙타형에서 snake_case 로 또는 그 반대로 변환하여 파이썬 코드를 작성하도록 유의하라.


이제 websocket 서버를 다시 시작하고, 이전과 같이 클라이언트를 연결한다. 클라이언트가 연결되면, BootNotification을 중앙시스템(서버)로 전송한다.

`[2, "12345", "BootNotification", {"chargePointVendor": "The Mobility House", "chargePointModel": "Optimus"}]`

서버가 응답하고, 아래와 같이 표시되어야 한다.

$ python -m websockets ws://localhost:9000/test_charge_point
Connected to ws://localhost:9000/test_charge_point.
> [2, "12345", "BootNotification", {"chargePointVendor": "The Mobility House", "chargePointModel": "Optimus"}]
< [3, "12345", {"currentTime": "2019-06-16T11:18:09.591716", "interval": 10, "status": "Accepted"}]`

축하!!. 중앙시스템(서버)를 만들었다.

이 문서에서 만든 서버 소스코드는 examples 디렉토리에서 찾을 수 있다.





2021년 1월 1일 금요일

러브앤드럭스 (Love & ohter drugs)

 앤 해서웨이는 ‘악마는 프라다를 입는다’ 라는 영화로 영어공부를 했기 때문에, 익히 알고 있었는데, 레미제라블이라는 뮤지컬 영화에서 코제트의 엄마(팡틴) 역할로 부르는 노래를 듣고 - 개성있게 생긴 친구가 연기 잘하는건 알았는데, 노래도 매우 잘하네! 하고, 인상 깊게 남게 되었다.

다른 앤 해서웨이의 작품을 찾다 보던 중에 알게된, 러브 앤 드럭스(2010) 라는 영화를 방금 보았다. (포스터에 끌려서 본건 아님. 아무튼 절대 아님. ㅎㅎ)

파킨슨병을 앓고있는 매기머독(앤 해서웨이)과, 여성들에게 인기많은 제이미 랜달(제이크 질런홀)의 관계를 시간의 흐름에 따라 잘 풀어낸 영화였다. 하지만 한국인의 정서와는 많이 다른 미국식 인간관계와, 그들의 문화를 모른다면 이해할 수 없는 순간순간의 농담들을 자막으로 전달하는데는 한계가 있을 수 밖에 없었던것 같다.  또, 남녀간의 사랑얘기이기 때문에, 베드씬은 빠질 수 없는 장면이고, 수위 높은 노출때문에 부모들과 자녀들이 함께 보기에는 어려워보인다.

어린 나이의 내가 봤다면, 앤 해서웨이는 예쁘고, 제이크 질런홀은 너무 잘생겼고, 둘이 어려움을 극복하고 예쁘게 사랑을 확인하고, 평생 함께 할것을 약속했다는 해피앤딩의 영화로, 재밌게 잘 봤다 하는 감상과, 비아그라 광고 영환가? 하고 끝났을법 하다.

하지만, 이제, 만 나이로도 속일 수 없는, 나이 앞에 5자가 확실하게 고정되는 한해를 시작하는 현 시점에서, 만약 이런 커플이 내 눈앞에 있다면, 현실의 전쟁터를 둘이 잘 극복 해 낼 수 있을 것이라고 축복해 줄 수 있을까? 시카고 학회?에 참석했을 때, 수십년 파킨슨병을 앓고있는 아내를 지켜온, 선배 커플 남편분의 스쳐가는 조언이 최선이라고 생각된다. - ‘호텔로 돌아가서 당장 짐 싸서 떠나라. 나도 아내를 사랑하지만, 다시 하라면 절대 못한다.’

이럴땐 문득, 나도 세상의 때가 많이 뭍은건가 하는 생각이 강하게 든다. 감성보다는 논리, 이성 또는 산술적인 재산상의 문제 (미국인데, 여주인공은 의료보험도 없었다. - 우리나라 의료보험 만세..) 와 같은 복잡한 생각이 영화를 보는 내내 머릿속에서 떠나지 않았다.

여주인공의 남주인공에 대한 계속된 밀어내기는, 사랑하는 사람이 자신때문에 격게 될, 앞으로 닥쳐 올, 힘들고 고될 수 밖에 없는 미래의 상황으로 힘들껄 알고, 자신이 그에게 부담이되는 상황을 만드는걸 싫어서 일것이다. 이 또한, 전형적인, 개인주의와 독립심을 중요하게 생각하는, 미국식 사고방식으로 보여진다.

영화속의 사건 하나 하나, 세세한 장면들의 관계를 자세하게 풀고 싶어하는 감독의 노력이 충분히 보여지지만, 110분 정도의 러닝타임으로는 좀 부족했나보다. 대사 한마디 후, 바뀌는 다음장면이 왜 나와야 하는지 이해하는데 몇초 정도의 버퍼링이 걸리는 장면이 몇몇 있었다. 또, 앤 해서웨이의 몸 사리지 않는 과감한 노출이 있음에도 불구하고, 잔 감정선까지 잘 표현한 세밀한 연기가 돋보여졌다. (아.. 이런 주제넘는 감상평은 이제 자제 해야겠다.) 아무튼 혼자 또는 애인과 보기에 괜찮은 영화였다.

p.s. 영어공부 더 해야겠다. 의학적인 대화 내용은 전혀 알아들을 수가 없다. ㅜ.ㅜ