#!/usr/bin/python3.6 -s

# Copyright (c) 2019 Paolo Patruno <p.patruno@iperbole.bologna.it>
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 
# 1. Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
# 3. Neither the name of mosquitto nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'rmap.settings'
from django.conf import settings

#disable unwanted initialization and error management of kivy
os.environ['KIVY_DOC_INCLUDE'] = "1"

import time
from rmap import rabbitshovel
from django.core.exceptions import ObjectDoesNotExist
from rmap.utils import nint
from rmap import jsonrpc

from rmap import __version__
import rmap.settings
import argparse

import django
from django.core import serializers
import requests
import base64, datetime
import logging,traceback,re
import json
import dballe
import random

# go to share dir for virtualenv
ve=os.getenv("VIRTUAL_ENV")
if ve is not None:
    os.chdir(ve+"/share/rmap")
    
django.setup()

from rmap.stations.models import StationMetadata
from rmap.stations.models import Sensor, SensorType
import rmap.rmap_core
from django.db import IntegrityError
from django.contrib.auth.models import User


try:
    tmp=StationMetadata.objects.all()[0]
    tmp=Sensor.objects.all()[0]
    tmp=SensorType.objects.all()[0]
    
except django.db.utils.OperationalError:
    print ("ERROR Cannot find DB: run rmapctrl --syncdb")
    raise SystemExit(1)


def genpskkey():
    def randomchars(length):
        s = "1234567890ABCDEF"*30
        return "".join(random.sample(s,length))

    return randomchars(32)


parser = argparse.ArgumentParser(description='Configure rmap boards. Thera are some option that are action options and others that are parameter options')
parser.add_argument('--version', action='version', version="%(prog)s "+__version__)

parser.add_argument('--serial_monitor',action="store_true", help="serial monitor")
parser.add_argument('--mqtt_monitor',action="store_true", help="MQTT monitor")
parser.add_argument('--list_stations',action="store_true", help="list all stations, board and transports")
parser.add_argument('--list_boards',action="store_true", help="list all board and transports on station ")
parser.add_argument('--list_sensors',action="store_true", help="list all board on board ")
parser.add_argument('--configuser',action="store_true", help="configure new or old user (set/create user with password")
parser.add_argument('--wizard',action="store_true", help="wizard configuration tool (simple set user, password, location on station")
parser.add_argument('--delsensor',action="store_true", help="del sensor from module of station")
parser.add_argument('--delsensors',action="store_true", help="ATTENTIONS delete ALL sensor from module of station")
parser.add_argument('--addsensor',action="store_true", help="add sensor to module of station")
parser.add_argument('--addsensors_by_template',  default=None, choices=rmap.rmap_core.template_choices, help="configure sensor for a module of station by template")
parser.add_argument('--config_station',action="store_true", help="configure board connected to station")
parser.add_argument('--export_config_v3',action="store_true", help="export configuration to file for Stima V3")
parser.add_argument('--modify_station',action="store_true", help="modify station metadata")
parser.add_argument('--dump_station',action="store_true", help="dump station configuration in json format with natural keys")
parser.add_argument('--upload_to_server',action="store_true", help="upload configuration to server")
parser.add_argument('--download_from_server',action="store_true", help="download station configuration from server")
parser.add_argument('--stima_version',  default="3", choices=("3","4"), help="stima version")
parser.add_argument('--notification',action="store_true", help="use notification (no response expected) for JSRPC")
parser.add_argument('--rpc_mqtt_admin',action="store_true", help="execute admin RPC over MQTT")
parser.add_argument('--rpc_mqtt_admin_fdownload',action="store_true", help="add firmware download request to admin RPC over MQTT")
parser.add_argument('--rpc_mqtt_admin_cdownload',action="store_true", help="add configuration download request to admin RPC over MQTT")
parser.add_argument('--rpc_mqtt_reboot',action="store_true", help="execute reboot RPC over MQTT")
parser.add_argument('--rpc_mqtt_reboot_fupdate',action="store_true", help="execute reboot RPC over MQTT with firmware update request")
parser.add_argument('--rpc_mqtt_pinout',action="store_true", help="execute pinout RPC over MQTT")
parser.add_argument('--rpc_mqtt_pinout_P1',action="store_true", help="set status of Pin 1")
parser.add_argument('--rpc_mqtt_pinout_P2',action="store_true", help="set status of Pin 2")
parser.add_argument('--rpc_mqtt_recovery',action="store_true", help="execute recovery data RPC over MQTT")
try:
    parser.add_argument('--datetimestart', default=None, type=datetime.datetime.fromisoformat, help="recovery data starting from this date and time in iso format (2011-11-04T12:05:23)" )
    parser.add_argument('--datetimeend', default=None, type=datetime.datetime.fromisoformat, help="recovery data ending to this date and time in iso format (2011-11-05T18:06:23)" )
except:
    # python < 3.7
    def fromisoformat(isoformat):
        return datetime.datetime.strptime(isoformat, '%Y-%m-%dT%H:%M:%S')
    parser.add_argument('--datetimestart', default=None, type=fromisoformat, help="recovery data starting from this date and time in iso format (2011-11-04T12:05:23)" )
    parser.add_argument('--datetimeend', default=None, type=fromisoformat, help="recovery data ending to this date and time in iso format (2011-11-05T18:06:23)" )
parser.add_argument('--station_slug',  default=None, help="work on station defined by this slug")
parser.add_argument('--board_slug',  default=None, help="work on board defined by this slug")
parser.add_argument('--device',  default=None, help="use this device for serial transport")
parser.add_argument('--baudrate',  default=None, help="use this baudrate for serial transport")
parser.add_argument('--transport',  default="serial",choices=["serial","tcpip","amqp","mqtt","dummy"],help="work on this transport")
parser.add_argument('--host',  default="master",choices=['master', 'master2', 'master3', 'master4'],help="work on this named board only")
parser.add_argument('--lon',  default=None, help="longitude of station")
parser.add_argument('--lat',  default=None, help="latitude of station")
parser.add_argument('--height',  default=None, help="height of station in meters")
parser.add_argument('--stationname',  default=None, help="name of station")
parser.add_argument('--username',  default=None, help="username for authentication")
parser.add_argument('--password',  default=None, help="password for authentication")
parser.add_argument('--server',  default="rmap.cc", help="rmap server")
parser.add_argument('--queue',  default="rmap", help="queue for amqp broker")
parser.add_argument('--exchange',  default="rmap", help="exchange for amqp broker")
parser.add_argument('--bluetoothname',  default="HC-05", help="name of the bluetooth device")
#parser.add_argument('--sensortemplate',  default=None, help="sensor template definition")
parser.add_argument('--sensorname',  default=None, help="sensor name")
drivers=[]
for driver in Sensor.SENSOR_DRIVER_CHOICES:
    drivers.append(driver[0])
parser.add_argument('--driver',  default="TMP", help="sensor driver",choices=drivers)
types=[]
for type in SensorType.objects.all():
    types.append(type.type)
parser.add_argument('--type',  default="TMP", help="sensor type",choices=types)
parser.add_argument('--i2cbus',  default=1, help="i2c smbus number")
parser.add_argument('--address',  default=72, help="i2c sensor address")
parser.add_argument('--node',  default=1, help="RF24Network node")
parser.add_argument('--timerange',  default="254,0,0", help="sensor timerange (see tables)")
parser.add_argument('--level',  default="103,2000", help="sensor level (see tables)")

parser.add_argument('--addboard',action="store_true", help="add board to module of station")
                                                                                                                                                                       
from rmap.stations.models import Board
tmpboard=Board()
choices=[]
help=""
for choice,desc in tmpboard.STIMAV4_MODULE_TYPE_CHOICES:
    choices.append(choice)
    help+=str(choice)+":"+desc+","
parser.add_argument('--boardtype', default=0, type=int,choices=choices, help="board type code ("+help+")")
def auto_int(x):
        return int(x, 0)
parser.add_argument('--serialnumber', default=None, type=auto_int, help="board serialnumber (can be hex); required with multiple board of same type")

parser.add_argument('--canactivate',action="store_true", help="activate can/Cyphal transport")
parser.add_argument('--cannodeid', default=100, type=int, help="Cyphal node id")
parser.add_argument('--cansubject', help="Cyphal subject")
parser.add_argument('--cansubjectid', default=100, type=int, help="Cyphal subject id")
parser.add_argument('--cansamplerate',type=int,  default=60, help="can period to send data on the bus in seconds")

parser.add_argument('--serialactivate',action="store_true", help="activate serial transport")
parser.add_argument('--mqttactivate',action="store_true", help="activate mqtt transport")
parser.add_argument('--mqttusername',  default="", help="mqtt username")
parser.add_argument('--mqttpassword',  default=User.objects.make_random_password(), help="mqtt password")
parser.add_argument('--mqttpskkey',  default=genpskkey(), help="mqtt psk key (32 random chars/number)")
parser.add_argument('--mqttsamplerate',type=int,  default=5, help="mqtt samplerate for sensorsin seconds")
parser.add_argument('--mqttrootpath',  default="sample", help="mqtt topic root path")
parser.add_argument('--mqttmaintpath',  default="maint", help="mqtt topic maint path for admin RPC")
parser.add_argument('--network',  default="fixed", help="network (station type) usually fixed or mobile")

parser.add_argument('--bluetoothactivate', action="store_true", help="activate bluetooth transport")
parser.add_argument('--amqpactivate', action="store_true", help="activate amqp transport")
parser.add_argument('--amqpuser',  default="rmap", help="amqp user")
parser.add_argument('--amqppassword',  default="", help="amqp password")

parser.add_argument('--tcpipactivate', action="store_true", help="activate tcp/ip transport")
parser.add_argument('--tcpipname',  default="master", help="tcp/ip name")
parser.add_argument('--tcpipntpserver',  default="ntpserver", help="tcp/ip ntp server")
parser.add_argument('--tcpipgsmapn',  default="ibox.tim.it", help="apn for gsm access")
parser.add_argument('--tcpippppnumber',  default="*99#", help="ppp number for gsm access")

# ttn
parser.add_argument('--ttn_jsrpc',action="store_true", help="execute json remote procedure call over ttn lorawan transport")
parser.add_argument('--ttnhost',  default="eu.thethings.network",help="Host: <Region>.thethings.network, where <Region> is last part of the handler you registered your application to, e.g. eu")
parser.add_argument('--ttnappid',help="Application I D")
parser.add_argument('--ttndevid',help="Device ID")
parser.add_argument('--ttnpassword',help="Application Access Key")
parser.add_argument('--ttnsampletime',default=900,type=int,help="time in seconds between reports")
parser.add_argument('--ttnconfirmed', action="store_true", help="set whether the downlink should be confirmed by the device")
parser.add_argument('--ttnport', default=1,type=int, help="LoRaWAN FPort")
parser.add_argument('--ttnschedule', default="replace",choices=("replace","first","last"), help="By default, the downlink will replace the currently scheduled downlink, if any. It is also possible to schedule the downlink as the first or last item in a the downlink queue")
parser.add_argument('--amqpdebug', action="store_true", help="activate amqp (pika) debug messages")

args = parser.parse_args()


if (args.serial_monitor):

    if (args.username is None or
        args.station_slug is None or
        args.board_slug is None):
        print(" selected serial_monitor without username or station_slug or board_slug")
        raise SystemExit(1)

    board=Board.objects.get(stationmetadata__slug=args.station_slug,stationmetadata__user__username=args.username,slug=args.board_slug)
    print(">station:",board.stationmetadata.name, ">board: ", board.name," slug="+board.slug," active=",board.active)

    try:
        if ( board.transportserial.active):
            device= args.device if args.device else  board.transportserial.device
            baudrate= args.baudrate if args.baudrate else  board.transportserial.baudrate
            print("\tSerial Transport device=",device," baudrate=",baudrate)
            
            from serial.tools import miniterm
            import sys
            sys.argv=[sys.argv[0],]
            miniterm.main( 
                default_port=device, 
                default_baudrate=baudrate
            )
        else:
            print("\ttransport serial not active for this board")
                
    except ObjectDoesNotExist:
        print("\ttransport serial not present for this board")



TOPIC_RE_V1 = re.compile((
    r'^1/[^/]+/'
    r'(?P<user>[^/]+)/'
    r'(?P<ident>[^/]*)/'
    r'(?P<lon>[0-9-]+),'
    r'(?P<lat>[0-9-]+)/'
    r'(?P<rep>[^/]+)/'
    r'(?P<pind>[0-9]+|-),'
    r'(?P<p1>[0-9]+|-),'
    r'(?P<p2>[0-9]+|-)/'
    r'(?P<lt1>[0-9]+|-),'
    r'(?P<lv1>[0-9]+|-),'
    r'(?P<lt2>[0-9]+|-),'
    r'(?P<lv2>[0-9]+|-)/'
    r'(?P<var>B[0-9]{5})$'
))

def parse_topic_v1(topic):
    match = TOPIC_RE_V1.match(topic)
    if match is None:
        logging.info("SKIP topic V1: {}".format(topic))
        return None
    else:
        g = match.groupdict()
        return {
            "user": None if g["user"] == "" else g["user"],
            "ident": None if g["ident"] == "" else g["ident"],
            "lon": int(g["lon"]),
            "lat": int(g["lat"]),
            "rep_memo": g["rep"],
            "level": (
                None if g["lt1"] == "-" else int(g["lt1"]),
                None if g["lv1"] == "-" else int(g["lv1"]),
                None if g["lt2"] == "-" else int(g["lt2"]),
                None if g["lv2"] == "-" else int(g["lv2"]),
                ),
            "trange": (
                None if g["pind"] == "-" else int(g["pind"]),
                None if g["p1"] == "-" else int(g["p1"]),
                None if g["p2"] == "-" else int(g["p2"]),
            ),
            "var": g["var"],
        }
    
    
def parse_payload(payload):
    return json.loads(payload.decode("utf-8"))

def parse_message(topic, payload):
    t = parse_topic_v1(topic)

    if t is None:
        return None

    m = parse_payload(payload)
    msg = t.copy()
    msg["value"] = m["v"]
    if all([t["level"] != (None, None, None, None),
            t["trange"] != (None, None, None),]):
        if "t" in m:
            msg["datetime"] = datetime.datetime.strptime(m["t"], "%Y-%m-%dT%H:%M:%S")
        else:
            msg["datetime"] = None
            #msg["datetime"] = datetime.datetime.now()
    else:
        msg["datetime"] = None
        
    if "a" in m:
        msg["attributes"] = m["a"]
    else:
        msg["attributes"] = {}
            
    return msg

def on_connect(mqttc, obj, flags, rc):
    print("connect rc: " + str(rc))
    print ("subscribe to: ")
    print (mqttc.mqttrootpath)
    print (mqttc.mqttmaintpath)
    mqttc.subscribe([(mqttc.mqttrootpath, 0),(mqttc.mqttmaintpath, 0)])


def on_message(mqttc, obj, msg):
    print (50*"-","MESSAGE",50*"-")
    print("Message: " + msg.topic + " " + str(msg.qos) + " " + str(msg.payload))

    try:
        m = parse_message(msg.topic, msg.payload)
        if m is None:
            return
        print ("Parsed: ",m)

        print("User: ",m["user"])
        print("Ident: ",m["ident"])
        print("Network: ",m["rep_memo"])
        print("Lon: ",m["lon"]/100000," Lat: ",m["lat"]/100000)
        print("Datetime: ",m["datetime"])
        print("Level: " , dballe.describe_level(*(m["level"])))
        if m["trange"] != (None, None, None):
            print("Trange: ", dballe.describe_trange(*(m["trange"])))
        var = dballe.var(m["var"], m["value"])
        try:
            print ("Variable: ",var.info.desc,var.enqd(),var.info.unit)
        except TypeError:
            print ("Variable: ",var.info.desc,var.enqc(),var.info.unit)

        for b, v in m["attributes"].items():
            var=dballe.var(b, v)
            try:
                print ("Attribute: ",var.info.desc,var.enqd(),var.info.unit)
            except TypeError:
                print ("Attribute: ",var.info.desc,var.enqc(),var.info.unit)
                
            #msg = dballe.Message("generic")
            #if m["ident"] is not None:
            #    msg.set_named("ident", dballe.var("B01011", m["ident"]))
            #
            #msg.set_named("longitude", dballe.var("B06001", m["lon"]))
            #msg.set_named("latitude", dballe.var("B05001", m["lat"]))
            #msg.set_named("rep_memo", dballe.var("B01194", m["rep_memo"]))
            #
            #if m["datetime"] is not None:
            #    msg.set_named("year", dballe.var("B04001", m["datetime"].year))
            #    msg.set_named("month", dballe.var("B04002", m["datetime"].month))
            #    msg.set_named("day", dballe.var("B04003", m["datetime"].day))
            #    msg.set_named("hour", dballe.var("B04004", m["datetime"].hour))
            #    msg.set_named("minute", dballe.var("B04005", m["datetime"].minute))
            #    msg.set_named("second", dballe.var("B04006", m["datetime"].second))
            #
            #var = dballe.var(m["var"], m["value"])
            #
            #for b, v in m["attributes"].items():
            #    var.seta(dballe.var(b, v))
            #
            #msg.set(m["level"], m["trange"], var)
            #
            #exporter = dballe.Exporter(encoding="BUFR")
            #totalbody=exporter.to_binary(msg)
            #print (totalbody)

    except Exception:
        print("Message not RMAP V1 conforme")
        print(traceback.format_exc())

def on_publish(mqttc, obj, mid):
    print("mid: " + str(mid))


def on_subscribe(mqttc, obj, mid, granted_qos):
    print("Subscribed: " + str(mid) + " " + str(granted_qos))


def on_log(mqttc, obj, level, string):
    print(string)

            
if (args.mqtt_monitor):

    if (args.username is None or
        args.station_slug is None):
        print(" selected mqtt_monitor without username or station_slug")
        raise SystemExit(1)

    mystation=StationMetadata.objects.get(slug=args.station_slug,user__username=args.username)

    print("STATION:", mystation)

    for board in mystation.board_set.all():
        print(">board: ", board.name," slug="+board.slug," active=",board.active)
        try:
            if ( board.transportmqtt.active):
                print("\tMQTT Transport server=",board.transportmqtt.mqttserver," user=",board.transportmqtt.mqttuser)

                import paho.mqtt.client as mqtt

                mqttc = mqtt.Client()
                mqttc.on_message = on_message
                mqttc.on_connect = on_connect
                mqttc.on_publish = on_publish
                mqttc.on_subscribe = on_subscribe
                # Uncomment to enable debug messages
                # mqttc.on_log = on_log
                username=board.transportmqtt.mqttuser+"/"+mystation.slug+"/"+board.slug
                mqttc.username_pw_set(username=username, password=board.transportmqtt.mqttpassword)
                mqttc.connect(board.transportmqtt.mqttserver, 1883, 60)


                mqttc.mqttrootpath="1/"+mystation.mqttrootpath+"/"+board.transportmqtt.mqttuser\
                    +"/"+ ("" if mystation.ident is None else mystation.ident) +"/"\
                    +"%d,%d" % (nint(mystation.lon*100000),nint(mystation.lat*100000))\
                    +"/"+mystation.network+"/#"

                mqttc.mqttmaintpath="1/"+mystation.mqttmaintpath+"/"+board.transportmqtt.mqttuser\
                    +"/"+ ("" if mystation.ident is None else mystation.ident) +"/"\
                    +"%d,%d" % (nint(mystation.lon*100000),nint(mystation.lat*100000))\
                    +"/"+mystation.network+"/#"


                mqttc.loop_forever()

                
        except ObjectDoesNotExist:
            print("\ttransport mqtt not present for this board")

            
if (args.amqpdebug):
    import sys
    logger=logging.getLogger('pika')
    logger.setLevel(logging.INFO)
    logger.addHandler(logging.StreamHandler(sys.stderr))

#truncate lon and lat to have coordinate as RMAP specification
if not (args.lon is None): args.lon=rmap.rmap_core.truncate(args.lon,5)
if not (args.lat is None): args.lat=rmap.rmap_core.truncate(args.lat,5)

if args.configuser:

    if (args.username is None or
        args.password is None):
        print(" selected configuser without username or password")
        raise SystemExit(1)

    rmap.rmap_core.configuser(username=args.username,password=args.password)

    print("END of user configuration")

if args.wizard:

    if (args.username is None or
        args.password is None or
        args.lon is None or
        args.lat is None):
        print(" selected wizard without username or password or lat or lon or board")
        raise SystemExit(1)


    constantdata={}
    if not args.stationname is None:
        constantdata["B01019"]=args.stationname
    if not args.height is None:
        constantdata["B07030"]=str(int(float(args.height)*10))

    rmap.rmap_core.configdb(username=args.username,password=args.password,
                            station=args.station_slug,lat=args.lat,lon=args.lon,constantdata=constantdata,
                            network=args.network,
                            mqttusername=args.username,
                            mqttpassword=args.password,
                            mqttpskkey=args.mqttpskkey,
                            mqttserver=args.server,
                            mqttsamplerate=args.mqttsamplerate,
                            mqttrootpath=args.mqttrootpath,
                            mqttmaintpath=args.mqttmaintpath,
                            bluetoothname=args.bluetoothname,
                            amqpusername=args.username,
                            amqppassword=args.password,
                            amqpserver=args.server,
                            queue=args.queue,
                            exchange=args.exchange,
                            board=args.board_slug,
                            activate=True,
                            stationname=args.stationname)

    print("END of wizard configuration")


if args.addboard:

    if (args.station_slug is None or
        args.board_slug is None or
        args.username is None):
        print(" selected addboard without station_slug or board_slug or username")
        raise SystemExit(1)

    rmap.rmap_core.addboard(station_slug=args.station_slug,username=args.username,board_slug=args.board_slug
                            ,type=args.boardtype,sn=args.serialnumber,activate=True
                            ,serialactivate=args.serialactivate
                            ,canactivate=args.canactivate,cannodeid=args.cannodeid,cansubject=args.cansubject,cansubjectid=args.cansubjectid,cansamplerate=args.cansamplerate
                            ,mqttactivate=args.mqttactivate, mqttserver=args.server
                            ,mqttusername=args.mqttusername, mqttpassword=args.mqttpassword,mqttpskkey=args.mqttpskkey, mqttsamplerate=args.mqttsamplerate
                            ,bluetoothactivate=args.bluetoothactivate, bluetoothname=args.bluetoothname
                            ,amqpactivate=args.amqpactivate, amqpusername=args.amqpuser, amqppassword=args.amqppassword
                            ,amqpserver=args.server, queue="rmap", exchange="rmap"
                            ,tcpipactivate=args.tcpipactivate, tcpipname=args.tcpipname, tcpipntpserver=args.tcpipntpserver
                            , tcpipgsmapn=args.tcpipgsmapn,tcpippppnumber=args.tcpippppnumber)


if args.delsensor:

    if (   args.sensorname is None
        or args.station_slug is None
        or args.username is None
        or args.board_slug is None):

        print(" selected delsensor without  station_slug or username or board_slug or sensorname")
        raise SystemExit(1)

    rmap.rmap_core.delsensor(station_slug=args.station_slug,username=args.username,board_slug=args.board_slug,name=args.sensorname)

    print("END of delsensor configuration")


if args.delsensors:

    if (args.board_slug is None
        or args.station_slug is None
        or args.username is None):

        print(" selected delsensor without station_slug or username or board_slug")
        raise SystemExit(1)

    rmap.rmap_core.delsensors(station_slug=args.station_slug,username=args.username,board_slug=args.board_slug)

    print("END of delsensors configuration")



if args.addsensor:

    if (   args.sensorname is None
        or args.station_slug is None
        or args.username is None
        or args.board_slug is None):

        print(" selected addsensor without sensorname or station_slug or username orboard_slug")
        raise SystemExit(1)


    rmap.rmap_core.addsensor(station_slug=args.station_slug,username=args.username,board_slug=args.board_slug,name=args.sensorname,driver=args.driver,
                             type=args.type,i2cbus=args.i2cbus,address=args.address,node=args.node,
                             timerange=args.timerange,level=args.level)
    #,sensortemplate=args.sensortemplate)

    print("END of addsensor configuration")


if args.addsensors_by_template is not None:

    if (args.board_slug is None
        or args.station_slug is None
        or args.username is None):

        print(" selected addsensors_by_template without station_slug or username or board_slug")
        raise SystemExit(1)


    rmap.rmap_core.addsensors_by_template(args.station_slug,args.username,args.board_slug,args.addsensors_by_template)

    print("END of addsensors_by_template configuration")


if args.list_stations:

    if (args.username is None):   
        for mystation in StationMetadata.objects.all():
            print("STATION:", mystation)
            print("slug=",mystation.slug)
    else:
        for mystation in StationMetadata.objects.filter(user__username=args.username):
            print("STATION:", mystation)
            print("slug=",mystation.slug)
            
    print("END of station list")
    #raise SystemExit(0)

if args.list_boards:


    if (args.username is None or
        args.station_slug is None):
        print(" selected list_boards without username or station_slug")
        raise SystemExit(1)


    mystation=StationMetadata.objects.get(slug=args.station_slug,user__username=args.username)

    print("STATION:", mystation)

    for board in mystation.board_set.all():
        print(">board: ", board.name," slug="+board.slug," active=",board.active)
        try:
            if ( board.transportserial.active):
                print("\tSerial Transport device=",board.transportserial.device," baudrate=",board.transportserial.baudrate)
                
        except ObjectDoesNotExist:
            print("\ttransport serial not present for this board")


        try:
            if ( board.transporttcpip.active):
                print("\tTCP/IP Transport", " hostname=",board.transporttcpip.name)

        except ObjectDoesNotExist:

            print("\ttransport TCPIP not present for this board")

        try:
            if ( board.transportamqp.active):
                print("\tAMQP Transport", " amqpserver=",board.transportamqp.amqpserver, end=' ')
                " exchange=",board.transportamqp.exchange, "queue=",board.transportamqp.queue

        except ObjectDoesNotExist:

            print("\ttransport AMQP not present for this board")


    print("END of board list")
    #raise SystemExit(0)


if args.list_sensors:

    if (args.username is None or
        args.board_slug is None or
        args.station_slug is None):
        print(" selected list_boards without username or station_slug or board_slug")
        raise SystemExit(1)


    for mysensor in Sensor.objects.filter(board__slug=args.board_slug
                                ,board__stationmetadata__slug=args.station_slug
                                ,board__stationmetadata__user__username=args.username):
    
        print("SENSOR:", mysensor)
        print("active: ",mysensor.active)
        print("name: ", mysensor.name)
        print("driver: ", mysensor.driver)
        print("type: ", mysensor.type)
        print("i2cbus: ", mysensor.i2cbus)
        print("address: ", mysensor.address)
        print("node: ", mysensor.node)
        print("timerange: ", mysensor.timerange, " -> ", mysensor.describe_timerange())
        print("level: ", mysensor.level , " -> ",mysensor.describe_level())

    print("END of sensor list")
    #raise SystemExit(0)


if args.modify_station:
    rmap.rmap_core.modifystation(station_slug=args.station_slug,username=args.username,lon=args.lon,lat=args.lat)

    print("END of station modification")

    
if args.rpc_mqtt_reboot:

    if (args.username is None or
        args.station_slug is None):
        print(" selected rpc_mqtt_reboot without username or station_slug")
        raise SystemExit(1)

    params={}
    if (args.rpc_mqtt_reboot_fupdate):
        params["fupdate"]=args.rpc_mqtt_reboot_fupdate
    
    rmap.rmap_core.rpcMQTT(station_slug=args.station_slug,
                                 board_slug=args.board_slug,
                                 username=args.username,version=args.stima_version,
                                 reboot=params)

    print("END of rpc_mqtt_reboot")
    

if args.rpc_mqtt_admin:

    if (args.username is None or
        args.station_slug is None):
        print(" selected rpc_mqtt_admin without username or station_slug")
        raise SystemExit(1)

    params={}
    if (args.rpc_mqtt_admin_fdownload):
        params["fdownload"]=args.rpc_mqtt_admin_fdownload
    if (args.rpc_mqtt_admin_cdownload):
        params["cdownload"]=args.rpc_mqtt_admin_cdownload
    
    rmap.rmap_core.rpcMQTT(station_slug=args.station_slug,
                                 board_slug=args.board_slug,
                                 username=args.username,version=args.stima_version,
                                 admin=params)

    print("END of rpc_mqtt_admin")


if args.rpc_mqtt_pinout:

    if (args.username is None or
        args.station_slug is None):
        print(" selected rpc_mqtt_pinout without username or station_slug")
        raise SystemExit(1)

    rmap.rmap_core.rpcMQTT(station_slug=args.station_slug,
                                 board_slug=args.board_slug,
                                 username=args.username,version=args.stima_version,
                                 pinout=[{"n":1,"s":args.rpc_mqtt_pinout_P1},{"n":2,"s":args.rpc_mqtt_pinout_P2}])

    print("END of rpc_mqtt_pinout")


if args.rpc_mqtt_recovery:

    if (args.username is None or
        args.datetimestart is None or
        args.station_slug is None):
        print(" selected rpc_mqtt_reboot without username or station_slug or datetimestart")
        raise SystemExit(1)
    dts=[args.datetimestart.year,args.datetimestart.month,args.datetimestart.day,
         args.datetimestart.hour,args.datetimestart.minute,args.datetimestart.second]


    if (args.datetimeend is None ):
        rmap.rmap_core.rpcMQTT(station_slug=args.station_slug,
                               board_slug=args.board_slug,
                               username=args.username,version=args.stima_version,
                               recovery={"dts":dts})
    else:
        dte=[args.datetimeend.year,args.datetimeend.month,args.datetimeend.day,
             args.datetimeend.hour,args.datetimeend.minute,args.datetimeend.second]
    
        rmap.rmap_core.rpcMQTT(station_slug=args.station_slug,
                               board_slug=args.board_slug,
                               username=args.username,version=args.stima_version,
                               recovery={"dts":dts,"dte":dte})

    print("END of rpc_mqtt_recovery")

    

if args.config_station:

    if (args.username is None or
        args.station_slug is None):
        print(" selected config_station without username or station_slug")
        raise SystemExit(1)

    rmap.rmap_core.configstation(transport_name=args.transport,station_slug=args.station_slug,
                                 board_slug=args.board_slug,
                                 device=args.device,baudrate=args.baudrate,host=args.host,username=args.username,
                                 notification=args.notification,version=args.stima_version)

    print("END of station configuration")

if args.export_config_v3:

    if (args.username is None or
        args.station_slug is None):
        print(" selected export_config_v3 without username or station_slug")
        raise SystemExit(1)

    configuration=rmap.rmap_core.configstation_to_struct_v3(station_slug=args.station_slug,board_slug=args.board_slug,
                                 username=args.username)

    with open('config.cfg', 'wb') as file:
        file.write(configuration)
        
    print("END of export configuration")
    
    
if args.ttn_jsrpc:

    if (args.ttnappid is None or
        args.ttndevid is None):
        print(" selected ttn JSRPC without ttnappid or ttndevid")
        raise SystemExit(1)


    
    ttntransport=jsonrpc.TransportTTN(host=args.ttnhost,appid=args.ttnappid,devid=args.ttndevid,password=args.ttnpassword,confirmed=args.ttnconfirmed,port=args.ttnport,schedule=args.ttnschedule,logfunc=jsonrpc.log_stdout)
    
    rpcproxy = jsonrpc.ServerProxy( jsonrpc.JsonRpc20(),ttntransport)
    if (rpcproxy is None):
        print(">>>>>>> Error building ttn transport")
        raise SystemExit(1)
    else:

        print(">>>>>>> execute ttn JSRPC")

        sampletime=args.ttnsampletime
        save=True
        print("Sampletime: ",sampletime)
        print("Save: ",save)

        mydata={"sampletime":sampletime,"save":save}
        data=rmap.rmap_core.compact(1,mydata)
        print(data)
        
        #payload_raw="AQIDBA=="
        payload_raw=base64.encodestring(data)


        #string=simplejson.dumps(string)
        #print "payload: ",string
        

        print("configuresampletime",rpcproxy.configuresampletime(payload_raw=payload_raw ))

        print("END of ttn JSRPC")

    

if args.dump_station:

    if (args.username is None or
        args.station_slug is None):
        print(" selected dump station without username or station_slug")
        raise SystemExit(1)

    print(rmap.rmap_core.dumpstation(station_slug=args.station_slug,user=args.username))


if args.upload_to_server:

    if (args.username is None or
        args.password is None or
        args.station_slug is None):
        print(" selected upload_to_server without username or password or station_slug")
        raise SystemExit(1)

    if (not args.board_slug is None):
        print(" upload_to_server do not take in account board_slug parameter (work for a full station)")
        raise SystemExit(1)
    
    #rmap.rmap_core.sendjson2amqp(station=args.station_slug,user=args.username,host=args.server,password=args.password)
    rmap.rmap_core.sendjson2http(station=args.station_slug,user=args.username,host=args.server,password=args.password)

    print("END of upload_to_server configuration")

if args.download_from_server:

    if (args.username is None or
        args.station_slug is None):
        print(" selected download_from_server without username or station_slug")
        raise SystemExit(1)

    if (not args.board_slug is None):
        print(" download_from_server do not take in account board_slug parameter (work for a full station)")
        raise SystemExit(1)

    url = "https://"+args.server+"/stations/"+args.username+"/"+args.station_slug+"/json_dump"
    login_url = "https://"+args.server+"/registrazione/login/"

    print("get configuration from: {}".format(url))
    
    with requests.Session() as session:

        if args.password is None:
            print ("ALERT: no password provided so no login will be performed and no secrets data will be downloaded !!!")
        else:
            session.get(login_url)
            csrftoken = session.cookies['csrftoken']
            r = session.post(login_url, data={"username":args.username, "password":args.password,"csrfmiddlewaretoken":csrftoken, "next": "/"}, headers={"Referer":login_url})

            if r.status_code == 200:
                if r.text.find('<input type="password" name="password" required id="id_password">') != -1:
                    print("ALERT: Wrong username or password")
                    raise SystemExit(1)
            else:
                print ("Error code login {}".format(r.status_code))
                raise SystemExit(1)

        r = session.get(url)
        if r.status_code != 200:
            print ("Error code {}".format(r.status_code))
            raise SystemExit(1)

        body=r.text

    #print(body)

    try:
        user = User.objects.create_user(args.username, args.username+'@'+args.server, args.password)            
        #trap IntegrityError for user that already exist
    except IntegrityError:
        print("user already exist")
        pass
    except:
        raise
    else:
        print("user:", args.username, "created ", "without" if args.password is None else "with", "password")
    try:
        for deserialized_object in serializers.deserialize("json",body):

            if isinstance(deserialized_object.object,StationMetadata):
                try:
                    StationMetadata.objects.get(slug=deserialized_object.object.slug,ident=deserialized_object.object.ident
                                                ,lat=deserialized_object.object.lat,lon=deserialized_object.object.lon
                                                ,network=deserialized_object.object.network).delete()
                    print ("Removed preexistent: {}:{} from local DB".format(deserialized_object.object.slug,deserialized_object.object.ident))
                except ObjectDoesNotExist:
                    print ("StationMetadata do not exist in DB")

            try:
                print("save:",deserialized_object.object)
                deserialized_object.save()
            except Exception as e:
                print((" [E] Error saving in DB",e))

    except Exception as e:
        print(("error in deserialize object; skip it",e))
