This commit is contained in:
Wolfgang Hottgenroth 2021-08-19 16:39:29 +02:00
commit 690f0f3cf5
Signed by: wn
GPG Key ID: E49AF3B9EF6DD469
8 changed files with 288 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__/
ENV

38
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,38 @@
stages:
- check
- build
variables:
IMAGE_NAME: $CI_REGISTRY/$CI_PROJECT_PATH
check:
image: registry.hottis.de/dockerized/base-build-env:latest
stage: check
tags:
- hottis
- linux
- docker
rules:
- if: $CI_COMMIT_TAG
script:
- checksemver.py -v
--versionToValidate "${CI_COMMIT_TAG}"
--validateMessage
--messageToValidate "${CI_COMMIT_MESSAGE}"
build:
image: registry.hottis.de/dockerized/docker-bash:latest
stage: build
tags:
- hottis
- linux
- docker
script:
- docker build --tag $IMAGE_NAME:latest .
- if [ "$CI_COMMIT_TAG" != "" ]; then
docker tag $IMAGE_NAME:latest $IMAGE_NAME:${CI_COMMIT_TAG};
docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY;
docker push $IMAGE_NAME:latest;
docker push $IMAGE_NAME:${CI_COMMIT_TAG};
fi

41
Dockerfile Normal file
View File

@ -0,0 +1,41 @@
FROM python:latest
LABEL Maintainer="Wolfgang Hottgenroth wolfgang.hottgenroth@icloud.com"
LABEL ImageName="registry.hottis.de/wolutator/modbusservice"
ARG APP_DIR="/opt/app"
ARG CONF_DIR="${APP_DIR}/config"
RUN \
apt update && \
pip3 install loguru && \
pip3 install dateparser && \
pip3 install connexion && \
pip3 install connexion[swagger-ui] && \
pip3 install uwsgi && \
pip3 install flask-cors && \
pip3 install six && \
pip3 install pymodbus
RUN \
mkdir -p ${APP_DIR} && \
mkdir -p ${CONF_DIR} && \
useradd -d ${APP_DIR} -u 1000 user
COPY *.py ${APP_DIR}/
COPY openapi.yaml ${APP_DIR}/
COPY server.ini ${CONF_DIR}/
USER 1000:1000
WORKDIR ${APP_DIR}
VOLUME ${CONF_DIR}
EXPOSE 5000
EXPOSE 9191
CMD [ "uwsgi", "./config/server.ini" ]

87
api.py Normal file
View File

@ -0,0 +1,87 @@
import time
import connexion
import json
import werkzeug
import os
from loguru import logger
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.exceptions import ModbusIOException
#with open('/opt/app/config/authservice.key', 'r') as f:
# JWT_PRIV_KEY = f.read()
clients = {
"modbus1": "172.16.2.157"
}
clientConn = {}
class UnknownClientException (Exception): pass
class UnableToConnectException (Exception): pass
class ConnectionException (Exception): pass
class IllegalValueException (Exception): pass
def connect(name):
try:
if name not in clientConn:
clientConn[name] = ModbusTcpClient(clients[name])
if not clientConn[name].connect():
raise UnableToConnectException()
return clientConn[name]
except KeyError as e:
raise UnknownClientException(e)
def close(name):
try:
clientConn[name].close()
except KeyError as e:
raise UnknownClientException(e)
def listClients():
return [ {"name":k, "address":v} for k,v in clients.items() ]
def createClient(**args):
try:
body = args["body"]
name = body["name"]
address = body["address"]
return "Ok"
except KeyError as e:
raise werkzeug.exceptions.BadRequest("name or address missing")
except Exception as e:
raise werkzeug.exceptions.BadRequest("{}: {}".format(e.__class__.__name__, str(e)))
def readCoil(client, address):
try:
conn = connect(client)
res = conn.read_coils(address)
if isinstance(res, ModbusIOException):
raise ConnectionException()
return int(res.bits[0])
except Exception as e:
raise werkzeug.exceptions.BadRequest("{}: {}".format(e.__class__.__name__, str(e)))
finally:
close(client)
def writeCoil(client, address, **args):
try:
value = args["body"]
if value not in [ 0, 1 ]:
raise IllegalValueException("Value in body must be 0 or 1")
conn = connect(client)
res = conn.write_coil(address, value)
if isinstance(res, ModbusIOException):
raise ConnectionException()
return "Ok"
except Exception as e:
raise werkzeug.exceptions.BadRequest("{}: {}".format(e.__class__.__name__, str(e)))
finally:
close(client)

94
openapi.yaml Normal file
View File

@ -0,0 +1,94 @@
openapi: 3.0.0
info:
title: ModbusService
version: "0.1"
paths:
/client:
get:
tags: [ "Client" ]
summary: List all configured clients
operationId: api.listClients
responses:
'200':
description: All configured clients
content:
'application/json':
schema:
type: array
items:
$ref: '#/components/schemas/Client'
post:
tags: [ "Client" ]
summary: Create a new client
operationId: api.createClient
requestBody:
content:
'application/json':
schema:
$ref: '#/components/schemas/Client'
responses:
'200':
description: Client successfully created
/action/coil/{client}/{address}:
get:
tags: [ "Action" ]
summary: Return a coil of client
operationId: api.readCoil
parameters:
- name: client
in: path
required: true
schema:
type: string
- name: address
in: path
required: true
schema:
type: integer
responses:
'200':
description: Status of the coil
content:
'application/json':
schema:
type: integer
put:
tags: [ "Action" ]
summary: Set a coil of client
operationId: api.writeCoil
parameters:
- name: client
in: path
required: true
schema:
type: string
- name: address
in: path
required: true
schema:
type: integer
requestBody:
content:
'application/json':
schema:
type: number
responses:
'200':
description: Status of the coil
content:
'application/json':
schema:
type: number
components:
schemas:
Client:
description: Modbus Client
type: object
properties:
name:
type: string
address:
type: string

6
server.ini Normal file
View File

@ -0,0 +1,6 @@
[uwsgi]
http = :5000
wsgi-file = server.py
processes = 4
stats = :9191

12
server.py Normal file
View File

@ -0,0 +1,12 @@
import connexion
from flask_cors import CORS
# instantiate the webservice
app = connexion.App(__name__)
app.add_api('openapi.yaml', options = {"swagger_ui": True})
# CORSify it - otherwise Angular won't accept it
CORS(app.app)
# provide the webservice application to uwsgi
application = app.app

8
test.py Normal file
View File

@ -0,0 +1,8 @@
import connexion
import logging
logging.basicConfig(level=logging.DEBUG)
app = connexion.App('modbusservice')
app.add_api('./openapi.yaml')
app.run(port=8080)