#!/usr/bin/python3
# SPDX-License-Identifier: BSD-3-Clause
#
# Copyright 2022 Raritan Inc. All rights reserved.

import copy, sys, os
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/pdu-python-api")
from raritan.rpc import Agent, sensors
from raritan.rpc.peripheral import DeviceManager, GatewaySensorManager, ModbusCfg

ip = "10.0.42.2"
user = "admin"
pw = "raritan"

try:
    ip = sys.argv[1]
    user = sys.argv[2]
    pw = sys.argv[3]
except IndexError:
    pass # use defaults

agent = Agent("https", ip, user, pw, disable_certificate_verification=True)
mgr = DeviceManager("/model/peripheraldevicemanager", agent)
gwcfg = mgr.getGatewaySensorManager()


ClassState = GatewaySensorManager.StateSensorClass(
    classId = "State",
    type = sensors.Sensor.TypeSpec(
        sensors.Sensor.DISCRETE_ON_OFF,
        sensors.Sensor.CONTACT_CLOSURE
    )
)

ClassNumber = GatewaySensorManager.NumericSensorClass(
    classId = "Time",
    metadata = sensors.NumericSensor.MetaData(
        type = sensors.Sensor.TypeSpec(
            sensors.Sensor.NUMERIC,
            sensors.Sensor.UNSPECIFIED,
            sensors.Sensor.SECOND,
        ),
    ),
)

ClassVoltage = GatewaySensorManager.NumericSensorClass(
    classId = "Voltage",
    metadata = sensors.NumericSensor.MetaData(
        type = sensors.Sensor.TypeSpec(
            sensors.Sensor.NUMERIC,
            sensors.Sensor.VOLTAGE,
            sensors.Sensor.VOLT
        ),
        decdigits = 1,
        accuracy = 0.0,
        resolution = 1.0,
        tolerance = 0,
        noiseThreshold = 1,
        range = sensors.NumericSensor.Range(0, 32767),
        thresholdCaps = sensors.NumericSensor.ThresholdCapabilities(True, True, True, True),
    ),
)

ClassFrequency = GatewaySensorManager.NumericSensorClass(
    classId = "Frequency",
    metadata = sensors.NumericSensor.MetaData(
        type = sensors.Sensor.TypeSpec(
            sensors.Sensor.NUMERIC,
            sensors.Sensor.FREQUENCY,
            sensors.Sensor.HZ
        ),
        decdigits = 2,
        accuracy = 0.0,
        resolution = 0.01,
        tolerance = 0,
        noiseThreshold = 0.01,
        range = sensors.NumericSensor.Range(45, 65),
        thresholdCaps = sensors.NumericSensor.ThresholdCapabilities(True, True, True, True),
    ),
)

ClassPower = GatewaySensorManager.NumericSensorClass(
    classId = "Power",
    metadata = sensors.NumericSensor.MetaData(
        type = sensors.Sensor.TypeSpec(
            sensors.Sensor.NUMERIC,
            sensors.Sensor.POWER,
            sensors.Sensor.WATT
        ),
        decdigits = 2,
        accuracy = 0.0,
        resolution = 0.01,
        tolerance = 0,
        noiseThreshold = 0.01,
        range = sensors.NumericSensor.Range(0, 1e10),
        thresholdCaps = sensors.NumericSensor.ThresholdCapabilities(True, True, True, True),
    ),
)

ClassActiveEnergy = GatewaySensorManager.NumericSensorClass(
    classId = "ActiveEnergy",
    metadata = sensors.NumericSensor.MetaData(
        type = sensors.Sensor.TypeSpec(
            sensors.Sensor.NUMERIC,
            sensors.Sensor.ENERGY,
            sensors.Sensor.WATT_HOUR
        ),
        decdigits = 0,
        accuracy = 0.0,
        resolution = 1,
        tolerance = 0,
        noiseThreshold = 1,
        range = sensors.NumericSensor.Range(0, 1e10),
    ),
)

ClassApparentEnergy = copy.deepcopy(ClassActiveEnergy)
ClassApparentEnergy.classId = "ApparentEnergy"
ClassApparentEnergy.metadata.type = sensors.Sensor.TypeSpec(
        sensors.Sensor.NUMERIC,
        sensors.Sensor.ENERGY,
        sensors.Sensor.VOLT_AMP_HOUR
)

ClassReactiveEnergy = copy.deepcopy(ClassActiveEnergy)
ClassReactiveEnergy.classId = "ReactiveEnergy"
ClassReactiveEnergy.metadata.type = sensors.Sensor.TypeSpec(
        sensors.Sensor.NUMERIC,
        sensors.Sensor.ENERGY,
        sensors.Sensor.VOLT_AMP_REACTIVE_HOUR
)

RemoteDevice = GatewaySensorManager.RemoteModbusRTUDevice(
    deviceId = "DEV",
    name = "Janitza UMG 604",
    busInterface = "USB-1",
    busSettings = ModbusCfg.SerialSettings(
        baud = 19200,
        parity = ModbusCfg.SerialSettings.Parity.NONE,
        dataBits = 8,
        stopBits = 1
    ),
    unitId = 2,
    interframeDelayDeciChars = 0, # use default
)

SHORT = GatewaySensorManager.ModbusValueEncoding32(
    encodingId = "SHORT",
    type = GatewaySensorManager.EncodingType.INT,
)

UINT = GatewaySensorManager.ModbusValueEncoding32(
    encodingId = "UINT",
    type = GatewaySensorManager.EncodingType.UINT,
)

INT = GatewaySensorManager.ModbusValueEncoding32(
    encodingId = "INT",
    type = GatewaySensorManager.EncodingType.INT,
)

LONG64 = GatewaySensorManager.ModbusValueEncoding64(
    encodingId = "LONG64",
    type = GatewaySensorManager.EncodingType.INT,
)

DEVNAME = GatewaySensorManager.ModbusValueEncoding48(
    encodingId = "DEVNAME",
    type = GatewaySensorManager.EncodingType.UINT,
    interpretationRules = [
        GatewaySensorManager.InterpretationRuleRAW(
            interpretation = GatewaySensorManager.Interpretation.REJECT_DEVICE,
            value = int.from_bytes(bytes("umg604", 'ascii'), byteorder='big', signed=False),
            invertCondition = True,
        ),
    ],
)

REALTIME = GatewaySensorManager.ModbusValueEncoding64(
    encodingId = "REALTIME",
    type = GatewaySensorManager.EncodingType.INT,
    scalingFactor = 2 / 1e9,
)

FLOAT = GatewaySensorManager.ModbusValueEncoding32(
    encodingId = "FLOAT",
    type = GatewaySensorManager.EncodingType.IEEE754,
)

DOUBLE = GatewaySensorManager.ModbusValueEncoding64(
    encodingId = "DOUBLE",
    type = GatewaySensorManager.EncodingType.IEEE754,
)

S0 = GatewaySensorManager.ModbusSensor(
    deviceId = "DEV",
    classId = "Time",
    encodingId = "REALTIME",
    defaultName = "Real time",
    function = ModbusCfg.ModbusFunction.HOLDING_REGISTER,
    regAddr = 0
)

S4 = GatewaySensorManager.ModbusSensor(
    deviceId = "DEV",
    classId = "Time",
    encodingId = "INT",
    defaultName = "System time",
    function = ModbusCfg.ModbusFunction.HOLDING_REGISTER,
    regAddr = 4
)

S10072 = GatewaySensorManager.ModbusSensor(
    deviceId = "DEV",
    classId = "",
    encodingId = "DEVNAME",
    defaultName = "Device Name",
    function = ModbusCfg.ModbusFunction.HOLDING_REGISTER,
    regAddr = 10072
)

S10202 = GatewaySensorManager.ModbusSensor(
    deviceId = "DEV",
    classId = "State",
    encodingId = "INT",
    defaultName = "KEY1",
    function = ModbusCfg.ModbusFunction.HOLDING_REGISTER,
    regAddr = 10202
)

S10204 = GatewaySensorManager.ModbusSensor(
    deviceId = "DEV",
    classId = "State",
    encodingId = "INT",
    defaultName = "KEY2",
    function = ModbusCfg.ModbusFunction.HOLDING_REGISTER,
    regAddr = 10204
)

S19000 = GatewaySensorManager.ModbusSensor(
    deviceId = "DEV",
    classId = "Voltage",
    encodingId = "FLOAT",
    defaultName = "Voltage L1-N",
    function = ModbusCfg.ModbusFunction.HOLDING_REGISTER,
    regAddr = 19000
)

S19050 = GatewaySensorManager.ModbusSensor(
    deviceId = "DEV",
    classId = "Frequency",
    encodingId = "FLOAT",
    defaultName = "Measured frequency",
    function = ModbusCfg.ModbusFunction.HOLDING_REGISTER,
    regAddr = 19050
)

S19060 = GatewaySensorManager.ModbusSensor(
    deviceId = "DEV",
    classId = "ActiveEnergy",
    encodingId = "FLOAT",
    defaultName = "Real energy L1..L3e",
    function = ModbusCfg.ModbusFunction.HOLDING_REGISTER,
    regAddr = 19060
)

cfg = gwcfg.getConfiguration()

cfg["UMG604"] = GatewaySensorManager.ConfigurationPackage(
    name = "Janitza UMG 604",
    classes = [ ClassState, ClassNumber, ClassVoltage, ClassFrequency, ClassPower, ClassActiveEnergy, ClassApparentEnergy, ClassReactiveEnergy ],
    devices = [ RemoteDevice ],
    encodings = [ SHORT, UINT, INT, LONG64, FLOAT, DOUBLE, DEVNAME, REALTIME ],
    sensors = [ S0, S4, S10072, S10202, S10204, S19000, S19050, S19060 ]
)

ret = gwcfg.setConfiguration(cfg)

match ret:
    case GatewaySensorManager.ERR_CONFIG_INCONSISTENT:
        print("FAILED: Consistency check has failed (e.g. non-unique identifiers)")

    case GatewaySensorManager.ERR_CONFIG_STORAGE_FAILED:
        print("FAILED: Configuration cannot be stored.")

    case _:
        print("Configuration stored")
