1 Commits

Author SHA1 Message Date
2ed0f86fd4 clearbuf patch? 2020-06-22 17:30:13 +02:00
13 changed files with 396 additions and 819 deletions

View File

@@ -1,13 +0,0 @@
---
kind: pipeline
type: exec
name: default
platform:
os: linux
arch: amd64
steps:
- name: Run unit test
commands:
- python -m unittest lmwsip

9
.gitignore vendored
View File

@@ -1,8 +1,3 @@
*.pyc
*.sip
tmp
__pycache__ __pycache__
build lmwsip-test
dist *.sip
lmwsip_marceln.egg-info
test/__pycache__

View File

@@ -1 +0,0 @@
TODO

View File

@@ -34,41 +34,16 @@ Otherwise the connection fails.
### Library ### Library
#### Use send (low level)
``` python ``` python
from lmwsip import LmwSip from lmwsip import LmwSip
sip = LmwSip(ssl=True, host="sip-lmw.rws.nl", port=443) lmwsip = LmwSip(ssl=True, host="sip-lmw.ad.rws.nl", port=443)
sip.send("LI user,pass\r") lmwsip.send("LI user,pass\r")
print("< [%s]" % (sip.recv().strip('\r'))) print("< [%s]" % (lmwsip.recv().strip('\r')))
sip.send("TI LMW\r") lmwsip.send("TI LMW\r")
print("< [%s]" % (sip.recv().strip('\r'))) print("< [%s]" % (lmwsip.recv().strip('\r')))
sip.send("LO\r") lmwsip.send("LO\r")
print("< [%s]" % (sip.recv().strip('\r'))) print("< [%s]" % (lmwsip.recv().strip('\r')))
```
#### Use value
``` python
from lmwsip import LmwSip
sip = LmwSip("USER", "PASS")
print(sip.ti())
print(sip.value("WN", "HOEK", "H10"))
sip.logout()
```
#### Use timeseries
``` python
from lmwsip import LmwSip
from datetime import datetime, timedelta
from pprint import pprint
end = datetime.now()
start = end - timedelta(hours=1)
sip = LmwSip("USER", "PASS")
pprint(sip.timeSerie("WN", "HOEK", "H10", start, end).ts)
``` ```
### siprun ### siprun

View File

@@ -1,30 +0,0 @@
#!/usr/bin/env python3
from lmwsip import LmwSip
from datetime import datetime, timedelta
from pprint import pprint
def printLocPar(sip, proces, loc, par, start, end):
try:
r = sip.timeSerie(proces, loc, par, start, end)
except Exception as e:
print(e)
print("\n%s %s %s:\n" % (proces, loc, par))
pprint(r.ts)
def main():
end = datetime.now() + timedelta(minutes=10)
start = end - timedelta(hours=1)
try:
sip = LmwSip("<user>", "<pass>")
except Exception as e:
print(e)
printLocPar(sip, "WN", "HOEK", "H10", start, end)
printLocPar(sip, "WN", "LEG1", "Czz10", start, end)
printLocPar(sip, "VW", "HOEK", "H10V", start, end)
printLocPar(sip, "AS", "HOEK", "H10A", start, end)
if __name__ == "__main__":
main()

326
lmwsip.py Normal file
View File

@@ -0,0 +1,326 @@
"""Module to support the LmwSip class
See: LmwSip"""
import socket
import ssl
import select
import time
import re
import logging
class LmwSip:
"""Class to connect to the LMW Standard Interface prototcol (sip)
This class iplement connection to the Rijkswaterstaat Meetnet
Water (LMW) with the Standard Interface Protocol using the
Rijkswaterstaat Meetnet Infrastructuur (RMI).
https://waterberichtgeving.rws.nl/water-en-weer/metingen
Support for:
ti
cmd(wn, vw, as)
"""
def __init__(self, user=None, password=None,
host="sip-lmw.rws.nl", port=443, meetnet="LMW", ssl = True,
check_ssl = True, timeout = 10, log = None, clearbuf = False):
"""LmwSip(user, password, [host], [port], [meetnet], [ssl], [check_ssl], [timeout], [log])
user(optinal): Lmw user name
password(optional): Lmw password
host(optional): Default sip-lmw.rws.nl
port(optional): Default 443
meetnet(optional): Default LMW
ssl(optional): Default true
check_ssl(optional): true
timeout(optional): 10
log(optional): None
clearbuf(optinal): None
Opens the connection and logs in
"""
self.user = user
self.password = password
self.host = host
self.port = port
self.meetnet = meetnet
self.ssl = ssl
self.check_ssl = check_ssl
self.timeout = timeout
self.clearbuf = clearbuf
self._socket = None
if (log != None):
self.log = log
self.log.debug("LmwSip.init")
else:
try:
self.log = logging.getLogger("lmwsip")
self.log.debug("LmwSip.init: Start log")
except Exception as e:
print("Logger failed: %s" % e)
if (self.host != None):
self.connect()
if (self.user != None):
self.login()
def lasttime(self, parameter):
#
# Find the last valid 10 minute window.
# The measurement of 12:00 is avaiable at 12:05:30.
# Before 12:05:30 we should use 11:50:00.
#
# At 12:05:29 we substract 15:29 from the time!
#
# Also note that we use GMT. So we should add one hour
# because we use GMT +1 (MET, UTC-1)
#
if (parameter.find("10") != -1):
now=time.time()
dt = now%600
if (dt < 330):
now = 3000 + now - dt
else:
now = 3600 + now - dt
else:
#
# e.g. H1 use 30 seconds to calculate the time.
#
dt = now%600
if (dt < 30):
now = 3540 + now - dt
else:
now = 3600 + now - dt
time_of_day=time.strftime("%H:%M", time.gmtime(now))
return { "day": time.strftime("%d-%m-%Y", time.gmtime(now)),
"time_of_day": time.strftime("%H:%M", time.gmtime(now)) }
def connect(self):
"""connect()
connects to lmw with tcp using the values of the object creation.
"""
try:
self._tcp = socket.create_connection((self.host, self.port))
except Exception as e:
self.log.error("LmwSip.connect(%s, %s) failed: %s",
self.host, self.port, e)
raise LmwSipConnectError("LmwSip.connect: Socket create failed")
if (self.ssl):
try:
self._context = ssl.create_default_context()
self._context.check_hostname = self.check_ssl
self._ssl = self._context.wrap_socket(self._tcp,
server_hostname=self.host)
self._socket = self._ssl
except Exception as e:
self.log.error("LmwSip.connect setup ssl failed:\n%s", e)
raise LmwSipConnectError("LmwSip.connect: setup ssl failed")
else:
self._socket = self._tcp
self._socket.settimeout(self.timeout)
def closesocket(self):
"""Closes the socket and set the socket to None. Doesn't logout"""
try:
self.log.debug("LmwSip.closesocket")
self._socket.close()
except Exception as e:
pass
self._socket = None
def emptysocket(self):
"""emptysocket()
If clearbuf read all the data from the socket and ignore it.
"""
if self._socket != None and self.clearbuf:
try:
self._socket.settimeout(0)
b = self._socket.recv(4096)
except Exception as e:
pass
self._socket.settimeout(self.timeout)
def send(self, sipcmd):
"""send(sipcmd)
send a sip command to the server
"""
if self._socket != None:
try:
logcmd = sipcmd.strip('\r')
if re.match("^LI", logcmd, re.IGNORECASE):
logcmd = re.sub(",.*", ", ******", logcmd)
self.log.debug("LmwSip.send(%s)" % logcmd)
self.emptysocket()
self._socket.sendall(sipcmd.encode('ascii'))
except Exception as e:
self.log.error("LmwSip.send(%s) failed: %s", sipcmd, e)
self.closesocket()
raise LmwSipConnectError("LmwSip.send: Socket connection lost")
else:
self.log.warn("LmwSip.send: No connection")
def recv(self):
"""recv()
recieve a answer from the sip server
"""
buf=""
while self._socket != None and re.search("\r$", buf) == None:
try:
self.log.debug("LmwSip.recv: Waiting for data");
b = self._socket.recv(4096).decode('utf-8')
if (b == ""):
self.log.error("SipLmw.recv: socket closed")
self.closesocket()
raise LmwSipConnectError("LmwSip.recv: Socket close")
else:
buf += b
except Exception as e:
self.log.error("SipLmw.recv: socket timeout: %s", e)
self.closesocket()
raise LmwSipConnectError("LmwSip.recv: Socket timeout")
if self._socket == None:
self.log.warn("LmwSip.recv: No connection")
elif buf[0] != '!':
self.log.warn("LmwSip.recv: Sip error: %s" % buf.strip('\r'))
else:
self.log.debug("LmwSip.recv: result: %s" % buf.strip('\r'))
return(buf)
def login(self):
"""login()
Login lmw using the object creation user, password.
Raises a LmwLoginFailure exception on failure
"""
li="LI " + self.user + "," + self.password + "\r"
self.send(li)
d = self.recv()
if (d[0] != '!'):
raise LmwLoginFailure(self.user + ":" + d)
def ti(self):
"""ti()
Request the time from lmw and returns the string.
Raises a LmwCmdWarn of failure
"""
ti="TI " + self.meetnet + "\r"
self.send(ti)
d = self.recv()
return (d[2:-1])
def cmd(self, process, location, parameter, time_delta, day,
time_of_day, cmd_type="DATA"):
"""cmd(process, location, parameter, time_delta, day, time_of_day)
Send a cmd to LMW and returns the lmw string
process: <WN|VW|AS>
location: <lmw location (e.g. HOEK)>
parameter: <lmw parameter (e.g. H10)>
time_delta: <Time windows (max 23:59, e.g. +01:00>
day: <Date>
time_of_day: <Time>
cmd_type: [DATA|DATB|OORS|OORB|""]
Example:
lmw.cmd("WN", "HOEK", "H10", "+01:00", "13-08-2018", "16:00")
Returns:
The LMW answer string
"""
if (process == "AS"):
data=""
else:
data="," + cmd_type
cmdstr=process + " " + self.meetnet + "," + location + "," + \
parameter + "," + time_delta + "," + day + "," + \
time_of_day + data + "\r"
self.send(cmdstr)
d = self.recv()
if (d[0] != '!'):
raise LmwCmdWarn(cmdstr, d)
return (d[2:-1])
def value(self, process, location, parameter, day = None,
time_of_day = None):
"""value(process, location, parameter, [day], [time_of_day]):
Parameters:
process: <WN|VW|AS>
location: <lmw location (e.g. HOEK)>
parameter: <lmw parameter (e.g. H10)>
day: [date = now()]
time_of_day: [time = now()]
The default returns the last value.
Example:
lmw.data_string("WN", "HOEK", "H10")
Returns a single string value or None
"""
if (day == None or time_of_day == None):
last = self.lasttime(parameter)
if (day==None):
day=last["day"]
if (time_of_day==None):
time_of_day=last["time_of_day"]
res = self.cmd(process, location, parameter, "+00:00", day,
time_of_day, "DATA")
value=re.sub("/.*$", "", res)
if (value == "99999"):
value=""
elif (value == "-999999999"):
value=""
#
# We should check the "kwaliteit"
#
return(value)
def logout(self):
"""logout()
Logs of
"""
self.send("LO\r")
self.closesocket()
class LmwSipConnectError(Exception):
"""Connection exceptions for LmwSip"""
def __init__(self, message):
self.message = message
def __str__(self):
return(self.message)
class LmwLoginFailure(Exception):
"""Exception from LmwSip on login failure"""
def __init__(self, user, message):
self.user = user
self.message = message
def __str__(self):
return("Login with user %s failed: %s", self.user, self.message)
class LmwCmdWarn(Warning):
"""Exception fro LmwSip on a cmd"""
def __init__(self, cmd, message):
self.cmd = cmd
self.message = message
def __str__(self):
return("Cmd %s failed: %s", self.cmd, self.message)

View File

@@ -1,511 +0,0 @@
"""Module to support the LmwSip class
See: LmwSip"""
import socket
import ssl
import select
import time
import re
import logging
from datetime import datetime, timedelta
from dateutil import tz
class LmwSip:
"""Class to connect to the LMW Standard Interface prototcol (sip)
This class iplement connection to the Rijkswaterstaat Meetnet
Water (LMW) with the Standard Interface Protocol using the
Rijkswaterstaat Meetnet Infrastructuur (RMI).
https://waterberichtgeving.rws.nl/water-en-weer/metingen
Support for:
ti
cmd(wn, vw, as)
"""
def __init__(self, user=None, password=None,
host="sip-lmw.rws.nl", port=443, meetnet="LMW", ssl = True,
check_ssl = True, timeout = 10, log = None, cleartelnet = False,
reconnecttime=540):
"""LmwSip(user, password, [host], [port], [meetnet], [ssl], [check_ssl], [timeout], [log])
user(optinal): Lmw user name
password(optional): Lmw password
host(optional): Default sip-lmw.rws.nl
port(optional): Default 443
meetnet(optional): Default LMW
ssl(optional): Default true
check_ssl(optional): Default true
timeout(optional): Default 10
log(optional): Default None
cleartelnet(optional): Default False
reconnecttime(optional): Default 540
Opens the connection and logs in.
"""
self.user = user
self.password = password
self.host = host
self.port = port
self.meetnet = meetnet
self.ssl = ssl
self.check_ssl = check_ssl
self.timeout = timeout
self.cleartelnet = cleartelnet
self.reconnecttime = reconnecttime
self._connecttime = time.time()
self._socket = None
if (log != None):
self.log = log
self.log.debug("LmwSip.init(%s, **********, %s, %s, %s, %s, %s, %s, %s, %s)" %
(user, host, port, meetnet, ssl, check_ssl, timeout, cleartelnet, reconnecttime))
else:
try:
self.log = logging.getLogger("lmwsip")
self.log.debug("LmwSip.init: Start log")
except Exception as e:
print("Logger failed: %s" % e)
if (self.host != None):
self.connect()
if (self.user != None):
self.login()
else:
self.reconnecttime = 0
def lasttime(self, parameter):
#
# Find the last valid 10 minute window.
# The measurement of 12:00 is avaiable at 12:05:30.
# Before 12:05:30 we should use 11:50:00.
#
# At 12:05:29 we substract 15:29 from the time!
#
# Also note that we use GMT. So we should add one hour
# because we use GMT +1 (MET, UTC-1)
#
if (parameter.find("10") != -1):
now=time.time()
dt = now%600
if (dt < 330):
now = 3000 + now - dt
else:
now = 3600 + now - dt
else:
#
# e.g. H1 use 30 seconds to calculate the time.
#
dt = now%600
if (dt < 30):
now = 3540 + now - dt
else:
now = 3600 + now - dt
time_of_day=time.strftime("%H:%M", time.gmtime(now))
return { "day": time.strftime("%d-%m-%Y", time.gmtime(now)),
"time_of_day": time.strftime("%H:%M", time.gmtime(now)) }
def connect(self):
"""connect()
connects to lmw with tcp using the values of the object creation.
"""
try:
self._tcp = socket.create_connection((self.host, self.port))
self._connecttime = time.time()
except Exception as e:
self.log.error("LmwSip.connect(%s, %s) failed: %s",
self.host, self.port, e)
raise LmwSipConnectError("LmwSip.connect: Socket create failed")
if (self.ssl):
try:
self._context = ssl.create_default_context()
self._context.check_hostname = self.check_ssl
self._ssl = self._context.wrap_socket(self._tcp,
server_hostname=self.host)
self._socket = self._ssl
except Exception as e:
self.log.error("LmwSip.connect setup ssl failed:\n%s", e)
raise LmwSipConnectError("LmwSip.connect: setup ssl failed")
else:
self._socket = self._tcp
self._socket.settimeout(self.timeout)
def closesocket(self):
"""Closes the socket and set the socket to None. Doesn't logout"""
try:
self.log.debug("LmwSip.closesocket")
self._socket.close()
except Exception as e:
pass
self._socket = None
def reconnectcheck(self):
"""Checks if the connection is longer open than the reconnect time.
After this time a logout is sent and a new connection is created.
This prevents the 10 minute server timeout"""
if self.reconnecttime > 0:
ct = time.time() - self._connecttime
if ct > self.reconnecttime:
self.log.debug("LmwSip.reconnectcheck: reconnect after %i seconds" % ct)
#
# Disable check for the reconnect
#
self.reconnecttime = - self.reconnecttime
self.logout()
time.sleep(1)
self.connect()
self.login()
self.reconnecttime = - self.reconnecttime
def send(self, sipcmd):
"""send(sipcmd)
send a sip command to the server
"""
self.reconnectcheck()
if self._socket != None:
try:
logcmd = sipcmd.strip('\r')
if re.match("^LI", logcmd, re.IGNORECASE):
logcmd = re.sub(",.*", ", ******", logcmd)
self.log.debug("LmwSip.send(%s)" % logcmd)
self._socket.sendall(sipcmd.encode('ascii'))
except Exception as e:
self.log.error("LmwSip.send(%s) failed: %s" % (sipcmd, e))
self.closesocket()
raise LmwSipConnectError("LmwSip.send: Socket connection lost")
else:
self.log.warning("LmwSip.send: No connection")
def telnetheader(self, header):
a = b'\xff\xfd\x01\xff\xfd\x03\xff\xfd\x00\xff\xfc\x01\xff\xfb\x00'
self.log.debug("LmwSip.telnetheader(%s) --> %s" % (header, a))
try:
self._socket.sendall(a)
except Exception as e:
self.log.error("LmwSip.telnetheader(%s) --> %s failed: %s" % (header, a, e))
self.closesocket()
raise LmwSipConnectError("LmwSip.telnetheader: Socket connection lost")
def recv(self):
"""recv()
recieve a answer from the sip server
"""
bytebuf=b''
stringbuf=""
while (self._socket != None) and (stringbuf.find("\r") == -1):
try:
self.log.debug("LmwSip.recv: %s: Waiting for data" % self.cleartelnet);
bytebuf = self._socket.recv(4096)
self.log.debug("recv: bytebuf: %s" % bytebuf)
if self.cleartelnet:
if bytebuf[0] == 255:
bytebuf = b''
except Exception as e:
self.log.error("SipLmw.recv: socket timeout: %s", e)
self.closesocket()
raise LmwSipConnectError("LmwSip.recv: No data recieved")
try:
stringbuf += bytebuf.decode('utf-8')
self.log.debug("recv: stringbuf: %s" % stringbuf)
except Exception as e:
self.log.error("SipLmw.recv: decode error: %s", e)
self.closesocket()
raise LmwSipDecodeError("LmwSip.recv: decode error", bytebuf)
if self._socket == None:
self.log.warn("LmwSip.recv: No connection")
elif len(stringbuf) == 0:
self.log.warn("LmwSip.recv: No data")
elif stringbuf[0] != '!':
self.log.warn("LmwSip.recv: Sip error: %s" % stringbuf.strip('\r'))
else:
self.log.debug("LmwSip.recv: result: %s" % stringbuf.strip('\r'))
return(stringbuf)
def login(self):
"""login()
Login lmw using the object creation user, password.
Raises a LmwLoginFailure exception on failure
"""
li="LI " + self.user + "," + self.password + "\r"
self.send(li)
d = self.recv()
if (d[0] != '!'):
raise LmwLoginFailure(self.user + ":" + d)
def ti(self):
"""ti()
Request the time from lmw and returns the string.
Raises a LmwCmdWarn of failure
"""
ti="TI " + self.meetnet + "\r"
self.send(ti)
d = self.recv()
return (d[2:-1])
def cmd(self, process, location, parameter, time_delta, day,
time_of_day, cmd_type="DATA"):
"""cmd(process, location, parameter, time_delta, day, time_of_day)
Send a cmd to LMW and returns the lmw string
process: <WN|VW|AS>
location: <lmw location (e.g. HOEK)>
parameter: <lmw parameter (e.g. H10)>
time_delta: <Time windows (max 23:59, e.g. +01:00>
day: <Date>
time_of_day: <Time>
cmd_type: [DATA|DATB|OORS|OORB|""]
Example:
lmw.cmd("WN", "HOEK", "H10", "+01:00", "13-08-2018", "16:00")
Returns:
The LMW answer string
"""
if (process == "AS"):
data=""
else:
data="," + cmd_type
cmdstr=process + " " + self.meetnet + "," + location + "," + \
parameter + "," + time_delta + "," + day + "," + \
time_of_day + data + "\r"
self.send(cmdstr)
d = self.recv()
if (d[0] != '!'):
raise LmwCmdWarn(cmdstr, d)
return (d[2:-1])
def value(self, process, location, parameter, day = None,
time_of_day = None):
"""value(process, location, parameter, [day], [time_of_day]):
Parameters:
process: <WN|VW|AS>
location: <lmw location (e.g. HOEK)>
parameter: <lmw parameter (e.g. H10)>
day: [date = now()]
time_of_day: [time = now()]
The default returns the last value.
Example:
lmw.data_string("WN", "HOEK", "H10")
Returns a single string value or None
"""
if (day == None or time_of_day == None):
last = self.lasttime(parameter)
if (day==None):
day=last["day"]
if (time_of_day==None):
time_of_day=last["time_of_day"]
res = self.cmd(process, location, parameter, "+00:00", day,
time_of_day, "DATA")
value=re.sub("/.*$", "", res)
if (value == "99999"):
value=""
elif (value == "-999999999"):
value=""
#
# We should check the "kwaliteit"
#
return(value)
def _lmwdelta_(self, window):
h = 24*window.days + window.seconds // 3600
m = (window.seconds % 3600)//60
return("+%02i:%02i" % (h, m))
def _roundtime_(self, time, parameter):
if time.microsecond != 0:
time += timedelta(microseconds=1000000-time.microsecond)
if time.second != 0:
time += timedelta(seconds=60-time.second)
if (parameter.find("10") != -1) and (time.minute % 10 != 0):
time += timedelta(minutes=(10-time.minute%10))
return(time)
def timeSerie(self, process, location, parameter,
startTime, endTime, cmd_type="DATB"):
"""timeSerie(process, location, parameter, startTime, endTime, cmd_type="DATA")
Parameters:
process: <WN|VW|AS>
location: <lmw location (e.g. HOEK)>
parameter: <lmw parameter (e.g. H10)>
startTime: Start time (datetime)
endTime: End time (datetime)
cmd_type: [DATA|DATB]
startTime is rounded up to the next measurement time.
So 12:00:00.000001 --> 12:00:10.00.0
The times should have correct timezone information. Otherwise local timezone
is assumed. Timezones are converted to 'GMT+1' for the sip commands.
Example:
lmw.data_string("WN", "HOEK", "H10", ...)
Returns a LmwtimeSerie object
Errors:
startTime > endTime
endTime - startTime > 24 hour
now - startTime < 30 days
"""
startTime = self._roundtime_(startTime.astimezone(tz.gettz('GMT+1')), parameter)
endTime = endTime.astimezone(tz.gettz('GMT+1'))
if (parameter.find("10") != -1):
delta = timedelta(minutes=10)
else:
delta = timedelta(minutes=1)
if startTime > endTime:
self.log.warn("starttime > endtime: %s > %s", startTime, endTime)
raise sipTimeSeriesError(startTime, endTime,
"starttime > endtime")
if datetime.now(tz=tz.gettz('GMT+1')) - startTime > timedelta(days=30):
self.log.warn("now() - starttime > 30 days: %s", startTime)
raise sipTimeSeriesError(startTime, endTime,
"now - starttime > 30 days")
self.log.debug("LmwSip.timeSerie: startTime: %s" % startTime)
self.log.debug("LmwSip.timeSerie: endTime: %s" % endTime)
if process == "VW":
cmd_type="DATA"
res = lmwTimeSerie(startTime, delta, "")
while startTime <= endTime:
if endTime - startTime > timedelta(days=1):
window = timedelta(days=1) - delta
else:
window = endTime-startTime
values = self.cmd(process, location, parameter,
self._lmwdelta_(window),
startTime.strftime("%d-%m-%Y"),
startTime.strftime("%H:%M"),
cmd_type)
res.addvalues(startTime, values)
startTime += window + delta
return(res)
def logout(self):
"""logout()
Logs of
"""
self.send("LO\r")
self.closesocket()
class lmwTimeSerie:
"""Class for lmw results.
The result are in lmwTimeSerie.ts as array
[ <time1>, [<value1 a, value1 b, ...], kwaliteit1, additionele kwaliteit1],
[ <time2>, [<value2 a, value2 b, ...], kwaliteit2, additionele kwaliteit2],
...
Note:
* For most measurements there is only one value (e.g H10).
* Additionale kwaliteit is optional and may contain None.
* Result times in UTC
"""
def __init__(self, start, delta, values=""):
"""lmwTimeSerie(start, delta, values)
Create a lmwTimeSerie object with:
start: Start time
delta: Period of the measurements
values: lmw result string
"""
self.ts = []
self.delta = delta
if values != "":
self.addvalues(start, values)
def addvalues(self, start, values):
"""addvalues(start, delta, values)
Add values to a timeserie
start: Start time
delta: Period of the measurements
values: lmw result string
"""
start = start.astimezone(tz.gettz('UTC'))
for e in values.split(";"):
v = e.split("/")
v[0] = v[0].split(",")
if len(v) == 2:
v.append(None)
self.ts.append([start, v[0], v[1], v[2]])
start += self.delta
class sipTimeSeriesError(Exception):
"""Parameter errors for timeSeries"""
def __init__(self, startTime, endTime, message):
self.startTime = startTime
self.endTime = endTime
self.message = message
def __str__(self):
return("%s\n starttime: %s\n end time: %s" %
(self.message, self.startTime, self.endTime))
class LmwSipConnectError(Exception):
"""Connection exceptions for LmwSip"""
def __init__(self, message):
self.message = message
def __str__(self):
return(self.message)
class LmwSipDecodeError(Exception):
"""Connection exceptions for LmwSip"""
def __init__(self, message, buf):
self.message = message
self.buf = buf
def __str__(self):
return(self.message + ":" + buf)
class LmwLoginFailure(Exception):
"""Exception from LmwSip on login failure"""
def __init__(self, user, message):
self.user = user
self.message = message
def __str__(self):
return("Login with user %s failed: %s" % (self.user, self.message))
class LmwCmdWarn(Warning):
"""Exception fro LmwSip on a cmd"""
def __init__(self, cmd, message):
self.cmd = cmd.replace('\r', '')
self.message = message
def __str__(self):
return("Cmd %s failed: %s" %(self.cmd, self.message))

View File

@@ -1,22 +0,0 @@
import setuptools
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name="lmwsip-marceln", # Replace with your own username
version="0.0.1",
author="Marcel Nijenhof",
author_email="pip@pion.xs4all.nl",
description="Interface for the lmw sip protocol",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://marceln.org/git/Werk/lmwsip",
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: TODO",
"Operating System :: OS Independent",
],
python_requires='>=3.6',
)

97
siprun
View File

@@ -2,49 +2,74 @@
import sys import sys
import getopt import getopt
import argparse
from lmwsip import LmwSip from lmwsip import LmwSip
def main(): def usage():
lastTime=LmwSip.lasttime(None, "H10") print("siprun [-H] [-s] [-d <date>] [-t <time>] [-h <host>] [-p <port>] [<files>]")
parser = argparse.ArgumentParser(description="Run a sip file.") print("\t-H: Show usage")
parser.add_argument("-u", "--unencrypted", action="store_true", print("\t-s: SSL connection")
help="Run a sip connection without ssl") print("\t-d <date>: Date replacement string (2019-02-14)")
parser.add_argument("-c", "--cleartelnet", action="store_true", print("\t-t <time>: Time replacement string (17:00)")
help="Clear telnet protocol in tcp session") print("\t-h <host>: Connect to host")
parser.add_argument("-H", "--host", action='store', print("\t-p <port>: Connect to port")
default="sip-lmw.rws.nl", print("\t-<files>: LMW commando files")
help="Host to connect to")
parser.add_argument("-p", "--port", action='store', type=int, default=443,
help="Port to connect to")
parser.add_argument("-d", "--date", action='store',
default=lastTime["day"],
help="Date replacement string [DD-MM-YYYY]")
parser.add_argument("-t", "--time", action='store',
default=lastTime["time_of_day"],
help="Time replacement string [HH:MM]")
parser.add_argument("files", type=argparse.FileType('r'), nargs="+",
help="Sip files to run")
args = parser.parse_args()
def main():
try: try:
lmwsip = LmwSip(host=args.host, port=args.port, opts, args = getopt.getopt(sys.argv[1:], "sh:p:d:t:", ["help", "output="])
ssl=not args.unencrypted, except getopt.GetoptError as err:
cleartelnet=args.cleartelnet) # print help information and exit:
print(err) # will print something like "option -a not recognized"
usage()
sys.exit(2)
ssl=False
time=None
date=None
host=None
port=None
for o, a in opts:
if o == "-H":
usage()
sys.exit(0)
elif o == "-s":
ssl=True
elif o == "-d":
date=a
elif o == "-t":
time=a
elif o == "-h":
host=a
elif o == "-p":
port=a
if (host==None or port==None):
print("Set host and port")
usage()
sys.exit(3)
try:
lmwsip = LmwSip(ssl=ssl, host=host, port=port)
except Exception as e: except Exception as e:
print("Connect to lmw failed: %s" % e) print("Connect to lmw failed: %s" % e)
exit(1) exit(1)
for f in args.files: if (date == None or time == None):
for cmd in f: # We assume a 10 minut interval so we use H10
cmd = cmd.replace('{DATE}', args.date) r = lmwsip.lasttime("H10")
cmd = cmd.replace('{TIME}', args.time) if (date == None):
cmd = cmd.replace('\n', '\r') date = r["day"]
print("> [%s]" % (cmd.strip('\r'))) if (time == None):
try: time = r["time_of_day"]
lmwsip.send(cmd) for cmdfile in args:
print("< [%s]" % (lmwsip.recv().strip('\r'))) with open(cmdfile, "r") as f:
except: for cmd in f:
pass cmd = cmd.replace('{DATE}', date)
cmd = cmd.replace('{TIME}', time)
cmd = cmd.replace('\n', '\r')
print("> [%s]" % (cmd.strip('\r')))
try:
lmwsip.send(cmd)
print("< [%s]" % (lmwsip.recv().strip('\r')))
except:
pass
f.close()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1,51 +0,0 @@
#!/usr/bin/python
import unittest
import lmwsip
import stubSipServer
from datetime import datetime, timedelta
from dateutil import tz
class lmwsipTest(unittest.TestCase):
def setUp(self):
self.sipserver = stubSipServer.sipServer()
self.sipserver.run()
self.sip = lmwsip.LmwSip("USER", "PASS", "localhost",
self.sipserver.port, ssl=False)
def tearDown(self):
self.sipserver.kill()
self.sip.closesocket()
def test_sipobj(self):
self.assertEqual(type(self.sip), lmwsip.LmwSip)
def test_ti(self):
self.assertEqual(type(self.sip.ti()), str)
def test_cmd(self):
self.assertEqual(type(self.sip.cmd("WN", "DUMMY", "D10", "+00:59", "2020-01-01", "00:00")), str)
def test_logout(self):
self.assertEqual(self.sip.logout(), None)
def test_lmwTimeSerie(self):
timezone = tz.gettz('GMT+1')
res = self.sip.timeSerie("WN", "DUMMY", "D10",
datetime.now(timezone)-timedelta(minutes=60),
datetime.now(timezone))
self.assertEqual(type(res.ts), list)
self.assertEqual(len(res.ts), 6)
self.assertEqual(res.ts[1][1][0], '1')
def test_roundtime(self):
timezone = tz.gettz('GMT+1')
t1 = datetime(2020, 1, 1, 0, 10, 0, 0, timezone)
t2 = datetime(2020, 1, 1, 0, 0, 0, 1, timezone)
self.assertEqual(self.sip._roundtime_(t1, "D10"), t1)
self.assertEqual(self.sip._roundtime_(t2, "D10"), t1)
if __name__ == '__main__':
unittest.main()

View File

@@ -1,116 +0,0 @@
#!/usr/bin/python
"""A stub sipserver for testing lmwsip
This is a stub sipserver that implements a small subset of the sip
protocol to perform unit tests.
Implements the following commands:
CMD> LI USER,PASS
ANS< !
CMD> TI LMW
ANS< ! 20-JAN-01 00:00:00
CMD> WN LMW,DUMMY,D10,+HH:MM,yyyy-mm-dd,HH:MM,DATA
ANS< ! 1/10,;2/10;....
CMD> WN LMW,DUMMY,D10,+HH:MM,yyyy-mm-dd,HH:MM,DATB
ANS< ! 1/10/0;2/10/0;....
CMD> LO
ANS< !
All other commands result in a "?"
CMD> *
ANS< ? ERROR
Note:
for a WN command the time and date are ignored.
The duration is used to calculare the number of results to send.
The sip syntax for time is much flexibler.
The stub only support this format!
"""
import os
import time
import random
import socketserver
class sipProtocol(socketserver.BaseRequestHandler):
def match(self, m):
return(self.data.find(m.encode()) == 0)
def send(self, a):
a = "%s\r" % a
self.request.sendall(a.encode())
def read(self):
try:
self.data = self.request.recv(1024).strip()
except:
self.data = None
def meting(self):
res = ""
sep = "! "
if self.data[18:19] == b'0':
h = int(self.data[19:20].decode())
else:
h = int(self.data[18:20].decode())
if self.data[21:22] == b'0':
m = int(self.data[22:23].decode())
else:
m = int(self.data[21:23].decode())
aantal = 1+(60*h+m)//10
if self.data[-1:] == b'A':
data = "%i/10"
else:
data = "%i/10/0"
for i in range(aantal):
res += sep+data % i
sep=";"
self.send(res)
def handle(self):
self.read()
while self.data:
if self.match("LI USER,PASS"):
self.send("!")
elif self.match("TI LMW"):
self.send("! 20-JAN-01 00:00:00")
elif self.match("WN LMW,DUMMY,D10"):
self.meting()
elif self.match("LO"):
self.send("!")
else:
self.send("? ERROR")
self.read()
class sipServer(socketserver.TCPServer):
def __init__(self):
self.port = None
while self.port == None:
self.port = random.randint(20000, 50000)
try:
super(sipServer, self).__init__(("localhost", self.port), sipProtocol)
except:
self.port = None
def run(self):
self.pid = os.fork()
if self.pid == 0:
self.serve_forever()
def kill(self):
if self.pid != 0:
os.kill(self.pid, 15)
self.server_close()
if __name__ == '__main__':
pass