# -*- coding: utf-8 -*-

import time
from essx.essx_exception import ESSXTimeoutException, ESSXValueException, ESSXChecksumException, ESSXDeviceException
import eza2500
from essx import essx_util
import threading
from essx import essx_debug

class ESSXTypeOES:
    """
    Rest APIとデバイスのやりとりをするコントローラクラス
    """
    def __init__(self, dcdc_dev = None, bat_dev = None, dcdc_config = None, bat_config = None, ad1 = 0, ad2 = 1, name = None):
        self.dcdc_config = dcdc_config
        self.bat_config = bat_config

        self.dcdc_dev = dcdc_dev
        self.bat_dev = bat_dev
        self.ad1 = ad1
        self.ad2 = ad2
        self.name = name

        self.com0101 = eza2500.Command0101(self.dcdc_dev)
        self.com0201 = eza2500.Command0201(self.dcdc_dev)
        self.com0301 = eza2500.Command0301(self.dcdc_dev)
        self.com0304 = eza2500.Command0304(self.dcdc_dev)
        self.com0401 = eza2500.Command0401(self.dcdc_dev)
        self.com0601 = eza2500.Command0601(self.dcdc_dev)
        self.com0701 = eza2500.Command0701(self.dcdc_dev)
        self.com0901 = eza2500.Command0901(self.dcdc_dev)
        self.com1001 = eza2500.Command1001(self.dcdc_dev)

        self.com0104 = eza2500.Command0104(self.dcdc_dev)
        self.com0404 = eza2500.Command0404(self.dcdc_dev)
        self.com0604 = eza2500.Command0604(self.dcdc_dev)
        self.com0704 = eza2500.Command0704(self.dcdc_dev)
        self.com0904 = eza2500.Command0904(self.dcdc_dev)

        self.battery_watchdog_running = True
        self.battery_watchdog_thread = threading.Thread(target = self._battery_watchdog)
        self.battery_watchdog_thread.daemon = True
        self.battery_watchdog_thread.start()


    def vrfy(self, obj, params = {}):
        """
        コマンドのパラメータが正しいかどうかを確認する。
        """
        obj.pack_senddata(self.ad1, self.ad2, params)

    def run(self, obj, params = {}):
        """
        コマンドを送って/受信をする。
        エラーが発生したらリトライをする。
        リトライ回数はEssCommConfigで設定する
        """

        if 'number_of_dcdc_error_retry' in self.dcdc_config:
            retry = int(self.dcdc_config['number_of_dcdc_error_retry'])
        else:
            retry = 0
        if 'number_of_timeout_retry' in self.dcdc_config:
            timeout_retry = int(self.dcdc_config['number_of_timeout_retry'])
        else:
            timeout_retry = 0

        if 'wait_retry' in self.dcdc_config:
            wait_retry = int(self.dcdc_config['wait_retry'])
        else:
            wait_retry = 0.1

        retry += 1
        timeout_retry += 1
        while retry > 0 and timeout_retry > 0:
            try:
                obj.send(self.ad1, self.ad2, params)
                obj.recv()
                time.sleep(0.062)
                return
            except ESSXTimeoutException as err:
                print("timeout retry: " + str(timeout_retry))
                timeout_retry = timeout_retry - 1
                if timeout_retry == 0:
                    raise
                time.sleep(wait_retry)
            except ESSXValueException as err:
                print("value exception    retry: " + str(retry))
                retry = retry - 1
                if retry == 0:
                    raise
                time.sleep(wait_retry)
            except ESSXChecksumException as err:
                print("checksum exception    retry: " + str(retry))
                retry = retry - 1
                if retry == 0:
                    raise
                time.sleep(wait_retry)
            except ESSXDeviceException as err:
                _ercd = obj.response['ercd']
                if _ercd == 0xffff: #受信したコマンドをサポートしていない
                    raise
                elif _ercd == 0xfffe: #コマンドのパラメータが不正
                    raise
                elif _ercd == 0xfffd: #正常だが受付不能
                    pass
                elif _ercd == 0xfffc: #システムビジー
                    pass
                elif _ercd == 0xfffb: #内部通信異常
                    raise
                retry = retry - 1
                if retry == 0:
                    raise ESSXTimeoutException('timeout')
                time.sleep(wait_retry)
        #ここには到達しないはずだが保険
        raise ESSXTimeoutException('timeout')

    def _check_battery(self):
        if self.bat_dev == None:
            return (0, 0, -1)
        else:
            return self.bat_dev.check_battery()
#        rsoc = 0
#        bos = 0
#        comm_err1 = -1
#        comm_err2 = -1
#
#        retry = 2
#        while retry > 0 and self.bat_dev != None:
#            mes = self.bat_dev.read_rsoc()
#            #一分内のデータか？(この値が適切かは考慮するところ)
#            if mes != None and mes.timestamp > time.time() - 60:
#                rsoc = (mes.data[2] + mes.data[3] * 256) / 10.0
#                comm_err1 = 0
#                break
#            else:
#                self.bat_dev.remote_rsoc()
#                time.sleep(0.5)
#                retry = retry - 1
#                rsoc = 0
#
#        retry = 2
#        while retry > 0 and self.bat_dev != None:
#            mes = self.bat_dev.read_status()
#            #一分内のデータか？(この値が適切かは考慮するところ)
#            if mes != None and mes.timestamp > time.time() - 60:
#                if mes.data[0] == 1:
#                    bos = 3
#                else:
#                    bos = 0
#                comm_err2 = 0
#                break
#            else:
#                self.bat_dev.remote_status()
#                time.sleep(0.5)
#                retry = retry - 1
#                bos = 0
#
#        if comm_err1 == 0 and comm_err2 == 0:
#            comm_err = 0
#        else:
#            comm_err = -1
#        return (rsoc, bos, comm_err)

    def _battery_watchdog(self):
        """
        10秒に一回バッテリの融通チェックをする
        """
        essx_debug.log("watchdog start")
        while self.battery_watchdog_running:
            if self.bat_config == None:
                time.sleep(10)
                continue
            essx_debug.log("watchdog")
            essx_debug.log(self.bat_config['config'])
            if 'force_dcdc_waiting' in self.bat_config['config'] and self.bat_config['config']['force_dcdc_waiting'] == True:
                (rsoc, bos, comm_err) = self._check_battery()
                essx_debug.log("rsoc " +str(rsoc))
                essx_debug.log("bos " +str(bos))
                if bos == 0:
                    essx_debug.log("->waiting")
                    self.run(self.com0104, {
                        'mode': self.checkOperationMode_2500(0)
                    })
            time.sleep(10)
        essx_debug.log("watchdog stop")

    # /1/log/data
    def log_data(self, params):
        _dcdc_conf = self.dcdc_config['config']
        _bat_conf = self.bat_config['config']

        (rsoc, bos, comm_err) = self._check_battery()

        if comm_err == -1:
            raise ESSXDeviceException('comm error')
#        if bos == 0:
#            if 'force_dcdc_waiting' in self.bat_config['config'] and self.bat_config['config']['force_dcdc_waiting'] == True:
#                self.run(self.com0104, {
#                        'mode': self.checkOperationMode_2500(0)
#                })

        return {
            "system_time": {
                "year": 0,
                "month": 0,
                "day": 0,
                "hour": 0,
                "minute": 0
            },
            "rsoc": float(rsoc),
            "dischargeable_time": {
                "hour": 0,
                "minute": 0
            },
            "battery_voltage": _bat_conf['battery_voltage'],
            "battery_current": _bat_conf['battery_current'],
            "battery_rsoc" : float(rsoc),
            "battery_status": 0,
            "battery_warning": 0,
            #"battery_alarm": 0
            "battery_operation_status": bos,
            "battery_comm_err": comm_err,
            "charge_discharge_power": _bat_conf['battery_voltage'] * _bat_conf['battery_current'],
            "ups_operation_schedule": 0,
            "ups_operation_mode": {
                "mode": 0,
                "parameter": 0,
                "stop_mode": 0
            },
            #"ups_input_voltage": res["ac_input_voltage"],
            #"ups_output_voltage": res["ac_output_voltage"],
            #"ups_output_current": res["ac_output_current_r"] + res["ac_output_current_s"],
            #"ups_output_frequency": res["ac_output_frequency"],
            #"ups_output_power": res["ac_output_power"],
            #"pvc_charge_voltage": res["pvc_charge_voltage"],
            #"pvc_charge_current": res["pvc_charge_current"],
            #"pvc_charge_power": res["pvc_charge_power"],
            #"pvc_alarm": res["pvc_alarm"],
            #"ups_alarm": res["inverter_alarm"]
        }

    def remote_ioctl_set(self, params):
        _conf = self.dcdc_config['config']
        # EZA2500は 6-4で DIG, CIBを指定する必要がある。CIBは設定より得られるが
        # DIGは設定より得られないので、6-1で今の値を取得する
        self.run(self.com0601)
        self.run(self.com0604, {
                'ubv': _conf['ubv'],
                'ugv': _conf['ugv'],
                'obv': _conf['obv'],
                'ogv': _conf['ogv'],
                'cib': _conf['cib'],
                'dig': self.com0601.response['dig']
        })
        self.run(self.com0704, {
            'bcf': essx_util.strToNum(_conf['bcf']),
            'cvb': _conf['cvb'], #tvb => cvb
            'dlb': _conf['dlb'], #lbv => dlb
            'cdb': _conf['cdb'], #cud => cdb
            'ddb': _conf['ddb'], #dld => ddb
        })
        self.run(self.com0304, {
            'cvb': _conf['cvb'],
            'drb': _conf['drb'],
        })

        res = {}
        res["dcdc_converter_name"] = self.name
        #設定の戻値ではなく設定した値そのものを返す
        res["dcdc_setup_parameter"] = {
            "ubv": _conf['ubv'],
            "ugv": _conf['ugv'],
            "obv": _conf['obv'],
            "ogv": _conf['ogv'],
            "bcf": "0x" + ("%04x" % _conf['bcf']),
            "cvb": _conf['cvb'],
            "dlb": _conf['dlb'],
            "cdb": _conf['cdb'],
            "ddb": _conf['ddb'],
            "drb": _conf['drb'],
            "cib": _conf['cib'],
            #"lbd": _conf['lbd']
        }
        return res

    def remote_ioctl_get(self, params):
        _res_log_data = self.log_data(params)
        _res_remote_get = self.remote_get(params)
        return {
            "status": _res_remote_get["status"],
            "powermeter": _res_remote_get["powermeter"],
            "meter": _res_remote_get["meter"],
            "vdis": _res_remote_get["vdis"],
            "param": _res_remote_get["param"],
            "system_time": _res_log_data["system_time"],
            "rsoc": _res_log_data["rsoc"],
            "dischargeable_time": _res_log_data["dischargeable_time"],
            "battery_voltage": _res_log_data["battery_voltage"],
            "battery_current": _res_log_data["battery_current"],
            "battery_rsoc" : _res_log_data["battery_rsoc"],
            "battery_status": _res_log_data["battery_status"],
            "battery_warning": _res_log_data["battery_warning"],
            #"battery_alarm": 0
            "battery_operation_status": _res_log_data["battery_operation_status"],
            "battery_comm_err": _res_log_data["battery_comm_err"],
            "charge_discharge_power": _res_log_data["charge_discharge_power"],
            "ups_operation_schedule": _res_log_data["ups_operation_schedule"],
            "ups_operation_mode": _res_log_data["ups_operation_mode"],
            #"ups_input_voltage": res["ac_input_voltage"],
            #"ups_output_voltage": res["ac_output_voltage"],
            #"ups_output_current": res["ac_output_current_r"] + res["ac_output_current_s"],
            #"ups_output_frequency": res["ac_output_frequency"],
            #"ups_output_power": res["ac_output_power"],
            #"pvc_charge_voltage": res["pvc_charge_voltage"],
            #"pvc_charge_current": res["pvc_charge_current"],
            #"pvc_charge_power": res["pvc_charge_power"],
            #"pvc_alarm": res["pvc_alarm"],
            #"ups_alarm": res["inverter_alarm"]
        }

    def remote_get(self, params):
        _conf = self.dcdc_config['config']
        self.run(self.com0101, {})
        self.run(self.com0201, {})
        self.run(self.com0401, {})
        self.run(self.com0901, {})
        self.run(self.com1001, {})
        self.run(self.com0601, {})

        res = {}

        res["operationMode"] = self.com0101.response['mode']
        res["alarmState"] = (self.com0201.response['cst'] & 0xc) >> 2
        res["alarm"] = (self.com0901.response['alm1'])
        res["status"] =    (self.com0201.response['cst'] & 0x3)
        res["wg"] = round(self.com1001.response['wg'], 16)
        res["tmp"] = round(self.com1001.response['tmp'],16) 
        res["vb"] = round(self.com1001.response['vb'], 16)
        res["wb"] = round(self.com1001.response['wb'], 16)
        res["vg"] = round(self.com1001.response['vg'], 16)
        res["ib"] = round(self.com1001.response['ib'], 16)
        res["ig"] = round(self.com1001.response['ig'], 16)
        res["dvg"] = round(self.com0401.response['dvg'], 16)  #tgv => dvg
        res["drg"] = round(self.com0401.response['drg'], 16)
        res["dig"] = round(self.com0601.response['dig'], 16) #lgc, lgd => dig


        return {
            "status": {
                "status": "0x" + ('%04x' % res["operationMode"]),
                "alarmState": essx_util.alarmStateStr(res["alarmState"]),
                "statusName": "Ignore",
                "alarm": essx_util.alarmStr(res["status"], res["alarmState"], res["alarm"]),
                "runningState": essx_util.runningStateStr(res['status']),
                "operationMode": essx_util.operationModeStr(res["operationMode"]),
            },
            "powermeter": {
                "p2": 0,
                "p1": 0,
                "v1": 0,
                "kwh2": 0,
                "kwh1": 0,
                "i1": 0
            },
            "meter": {
                "wg": res["wg"],
                "tmp": res["tmp"],
                "vb": res["vb"],
                "wb": res["wb"],
                "vg": res["vg"],
                "ib": res["ib"],
                "ig": res["ig"]
            },
            "vdis": {
                "dvg": res["dvg"],
                "drg": res["drg"]
            },
            "param": {
                'cib': _conf['cib'],
                'ubv': _conf['ubv'],
                "dig": res["dig"],
                'ogv': _conf['ogv'],
                'obv': _conf['obv'],
                'ugv': _conf['ugv'],
            }
        }

    def remote_get_status(self, params):
        self.run(self.com0101)
        self.run(self.com0201)
        self.run(self.com1001)

        res = {}
        res["operationMode"] = self.com0101.response['mode']
        res["alarmState"] = (self.com0201.response['cst'] & 0xc) >> 2
        res["status"] =    (self.com0201.response['cst'] & 0x3)
        res["wg"] = round(self.com1001.response['wg'], 16)
        res["tmp"] = round(self.com1001.response['tmp'], 16)
        res["vb"] = round(self.com1001.response['vb'], 16)
        res["wb"] = round(self.com1001.response['wb'], 16)
        res["vg"] = round(self.com1001.response['vg'], 16)
        res["ib"] = round(self.com1001.response['ib'], 16)
        res["ig"] = round(self.com1001.response['ig'], 16)


        return {
            "status": {
                "alarmState": essx_util.alarmStateStr(res["alarmState"]),
                "runningState": essx_util.runningStateStr(res['status']),
                "operationMode": essx_util.operationModeStr(res["operationMode"]),
            },
            "meter": {
                "wg": res["wg"],
                "tmp": res["tmp"],
                "vb": res["vb"],
                "wb": res["wb"],
                "vg": res["vg"],
                "ib": res["ib"],
                "ig": res["ig"]
            },
        }

    def remote_set(self, params):
        """
        以下のパラメータが必要である
        params['mode']
        params['dvg']
        params['drg']
        params['dig']

        'mode'は16進数の文字列(EZA2500値)で指定する。
        読み変えはしない
        """
        _conf = self.dcdc_config['config']

        _local_current_mode = params['current_mode'] ##############Added for v1.3 2018/11/14##################

        self.vrfy(self.com0404, {
            'dvg': params['dvg'], 'drg': params['drg']
        })

        self.vrfy(self.com0604, {
            'ubv': _conf['ubv'],
            'ugv': _conf['ugv'],
            'obv': _conf['obv'],
            'ogv': _conf['ogv'],
            'cib': _conf['cib'],
            'dig': params['dig'],
        })
        self.vrfy(self.com0104, {
            'mode': self.checkOperationMode_2500(essx_util.hexStrToInt(params['mode']))
        })

        #############Added for v1.3 2018/11/14 現在のModeによってコマンドの発行順を変える。###################
        ##########################################↓↓↓#########################################################
        if _local_current_mode  == '0x0000':
             self.run(self.com0404, {
                 'dvg': params['dvg'], 'drg': params['drg']
             })
             self.run(self.com0604, {
                 'ubv': _conf['ubv'],
                 'ugv': _conf['ugv'],
                 'obv': _conf['obv'],
                 'ogv': _conf['ogv'],
                 'cib': _conf['cib'],
                 'dig': params['dig'],
             })
             self.run(self.com0104, {
                 'mode': self.checkOperationMode_2500(essx_util.hexStrToInt(params['mode']))
             })
        else:
             self.run(self.com0104, {
                 'mode': self.checkOperationMode_2500(essx_util.hexStrToInt(params['mode']))
             })
             self.run(self.com0404, {
                 'dvg': params['dvg'], 'drg': params['drg']
             })
             self.run(self.com0604, {
                 'ubv': _conf['ubv'],
                 'ugv': _conf['ugv'],
                 'obv': _conf['obv'],
                 'ogv': _conf['ogv'],
                 'cib': _conf['cib'],
                 'dig': params['dig'],
             })
        ##########################################↑↑↑######################################################
        ###################################################################################################
        
        self.run(self.com0201)
        self.run(self.com0901)
        self.run(self.com1001)

        res = {}
        res["operationMode"] = self.com0104.response['mode']
        res["alarmState"] = (self.com0201.response['cst'] & 0xc) >> 2
        res["alarm"] = (self.com0901.response['alm1'])
        res["status"] = (self.com0201.response['cst'] & 0x3)
        res["wg"] = round(self.com1001.response['wg'], 16)
        res["tmp"] = round(self.com1001.response['tmp'], 16)
        res["vb"] = round(self.com1001.response['vb'], 16)
        res["wb"] = round(self.com1001.response['wb'], 16)
        res["vg"] = round(self.com1001.response['vg'], 16)
        res["ib"] = round(self.com1001.response['ib'], 16)
        res["ig"] = round(self.com1001.response['ig'], 16)
        res["dvg"] = round(self.com0404.response['dvg'], 16)
        res["drg"] = round(self.com0404.response['drg'], 16)
        res["dig"] = round(self.com0604.response['dig'], 16)

        return {
            "status": {
                "status": "0x" + ('%04x' % res["operationMode"]),
                "alarmState": essx_util.alarmStateStr(res["alarmState"]),
                "statusName": "Ignore",
                "alarm": essx_util.alarmStr(res["status"], res["alarmState"], res["alarm"]),
                "runningState": essx_util.runningStateStr(res['status']),
                "operationMode": essx_util.operationModeStr(res["operationMode"]),
            },
            "meter": {
                "wg": res["wg"],
                "tmp": res["tmp"],
                "vb": res["vb"],
                "wb": res["wb"],
                "vg": res["vg"],
                "ib": res["ib"],
                "ig": res["ig"]
            },
            "vdis": {
                "dvg": res["dvg"],
                "drg": res["drg"]
            },
            "param": {
                'cib': _conf['cib'],
                'ubv': _conf['ubv'],
                "dig": res["dig"],
                'ogv': _conf['ogv'],
                'obv': _conf['obv'],
                'ugv': _conf['ugv'],
            }
        }

    def remote_set_current(self, params):
        """
        digが必須
        mode, dvg, drg も上層より指定されてくるが必須ではない。
        """
        _conf = self.dcdc_config['config']

        self.vrfy(self.com0604, {
            'ubv': _conf['ubv'],
            'ugv': _conf['ugv'],
            'obv': _conf['obv'],
            'ogv': _conf['ogv'],
            'cib': _conf['cib'],
            'dig': params['dig'],
        })

        self.run(self.com0604, {
            'ubv': _conf['ubv'],
            'ugv': _conf['ugv'],
            'obv': _conf['obv'],
            'ogv': _conf['ogv'],
            'cib': _conf['cib'],
            'dig': params['dig'],
        })
        self.run(self.com1001)
        res = {}
        res["wg"] = round(self.com1001.response['wg'], 16)
        res["tmp"] = round(self.com1001.response['tmp'], 16)
        res["vb"] = round(self.com1001.response['vb'], 16)
        res["wb"] = round(self.com1001.response['wb'], 16)
        res["vg"] = round(self.com1001.response['vg'], 16)
        res["ib"] = round(self.com1001.response['ib'], 16)
        res["ig"] = round(self.com1001.response['ig'], 16)
        res["dig"] = round(self.com0604.response['dig'], 16)

        return {
            "meter": {
                "wg": res["wg"],
                "tmp": res["tmp"],
                "vb": res["vb"],
                "wb": res["wb"],
                "vg": res["vg"],
                "ib": res["ib"],
                "ig": res["ig"]
            },
            "param": {
                'cib': _conf['cib'],
                'ubv': _conf['ubv'],
                "dig": res["dig"],
                'ogv': _conf['ogv'],
                'obv': _conf['obv'],
                'ugv': _conf['ugv'],
            }
        }


    def remote_set_voltage(self, params):
        """
        dvg, drg が必須
        mode, digも上層より指定されてくるが必須ではない。
        """
        self.vrfy(self.com0404, {
            'dvg': params['dvg'], 'drg': params['drg']
        })
        self.run(self.com0404, {
            'dvg': params['dvg'], 'drg': params['drg']
        })

        self.run(self.com1001)
        res = {}
        res["wg"] = round(self.com1001.response['wg'], 16)
        res["tmp"] = round(self.com1001.response['tmp'], 16)
        res["vb"] = round(self.com1001.response['vb'], 16)
        res["wb"] = round(self.com1001.response['wb'], 16)
        res["vg"] = round(self.com1001.response['vg'], 16)
        res["ib"] = round(self.com1001.response['ib'], 16)
        res["ig"] = round(self.com1001.response['ig'], 16)
        res["dvg"] = round(self.com0404.response['dvg'], 16)
        res["drg"] = round(self.com0404.response['drg'], 16)

        return {
            "meter": {
                "wg": res["wg"],
                "tmp": res["tmp"],
                "vb": res["vb"],
                "wb": res["wb"],
                "vg": res["vg"],
                "ib": res["ib"],
                "ig": res["ig"]
            },
            "vdis": {
                "dvg": res["dvg"],
                "drg": res["drg"]
            },
        }

    def remote_ioctl_clr_alarm(self, params):
        self.run(self.com0904, {
            'd0': 0, 'd1': 0,
        })
        return self.remote_get(params)

    def checkOperationMode_2500(self, v):
        """
        EZA2500のoperationModeの確認
        """
        if v == 0x0:
            return v
        elif v == 0x2:
            return v
        elif v == 0x14:
            return v
        elif v == 0x41:
            return v
        raise ESSXValueException("bad value: operation mode: " + str(v))

if __name__    == "__main__":
    #単体テストのためにはデバイスの先に装置(esscomm/ess2/test.py で代用可)が必要
    import serial
    import traceback
    import essx

    ser_dev = essx.essx_rs232c.ESSXRS232C("/dev/cuaU1", 19200)
    dcdc_dev = eza2500.EZA2500Device(dev = ser_dev, timeout = 1)
    controller = ESSXType3(dcdc_dev = dcdc_dev, bat_dev = None, dcdc_config = {
        'config': {
            'bcf': 0,
#            'lbc': 45.8, #バッテリ上限電流(充電)
#            'lbd': 45.8, #バッテリ上限電流(放電)
            'ubv': 60,  #バッテリ低電圧閾値
            'obv': 60,  #バッテリ過電圧閾値
            #'lgc': 34.4, #グリッド上限電流(充電)
            #'lgd': 34.4, #グリッド上限電流(放電)
            'ugv': 280,  #グリッド低電圧閾値
            'ogv': 280,  #グリッド過電圧閾値
            #'tgv': 240,  #グリッド目標電圧
            'cvb': 40,  #バッテリ目標電圧
            'dlb': 40,  #バッテリ放電終止電圧
            'cdb': 6,   #バッテリ充電上限予告電圧偏差
            'ddb': 6,   #バッテリ充電終止予告電圧偏差
            #'drg': 0.25, #グリッドドループ率
            'drb': 0.25, #バッテリドループ率
            'cib': 45.8,
        }}, bat_config = {
            'config': {
                'battery_voltage': 200,
                'battery_current': 2.0
            }}
    )

    try:
        print("/1/log/data")
        print(controller.log_data({}))
        print("remote/ioctl/get")
        print(controller.remote_ioctl_get({}))
        print("remote/ioctl/set")
        print(controller.remote_ioctl_set({}))
        print(controller.remote_get({}))
        print(controller.remote_get_status({}))
        print(controller.remote_set({'mode': '0x02', 'dvg': 300, 'drg': 0.1, 'dig': 0}))
        print(controller.remote_set_current({'dig': 3}))
        print(controller.remote_set_voltage({'dvg': 360, 'drg': 0.2}))
        print(controller.remote_ioctl_clr_alarm({}))
    except ESSXValueException as err:
        print(err.reason)
        print(traceback.format_exc())
        raise err

# -*- coding: utf-8 -*-

import time
from essx.essx_exception import ESSXTimeoutException, ESSXValueException, ESSXChecksumException, ESSXDeviceException
import eza2500
from essx import essx_util
import threading
from essx import essx_debug

class ESSXTypeOES:
    """
    Rest APIとデバイスのやりとりをするコントローラクラス
    """
    def __init__(self, dcdc_dev = None, bat_dev = None, dcdc_config = None, bat_config = None, ad1 = 0, ad2 = 1, name = None):
        self.dcdc_config = dcdc_config
        self.bat_config = bat_config

        self.dcdc_dev = dcdc_dev
        self.bat_dev = bat_dev
        self.ad1 = ad1
        self.ad2 = ad2
        self.name = name

        self.com0101 = eza2500.Command0101(self.dcdc_dev)
        self.com0201 = eza2500.Command0201(self.dcdc_dev)
        self.com0301 = eza2500.Command0301(self.dcdc_dev)
        self.com0304 = eza2500.Command0304(self.dcdc_dev)
        self.com0401 = eza2500.Command0401(self.dcdc_dev)
        self.com0601 = eza2500.Command0601(self.dcdc_dev)
        self.com0701 = eza2500.Command0701(self.dcdc_dev)
        self.com0901 = eza2500.Command0901(self.dcdc_dev)
        self.com1001 = eza2500.Command1001(self.dcdc_dev)

        self.com0104 = eza2500.Command0104(self.dcdc_dev)
        self.com0404 = eza2500.Command0404(self.dcdc_dev)
        self.com0604 = eza2500.Command0604(self.dcdc_dev)
        self.com0704 = eza2500.Command0704(self.dcdc_dev)
        self.com0904 = eza2500.Command0904(self.dcdc_dev)

        self.battery_watchdog_running = True
        self.battery_watchdog_thread = threading.Thread(target = self._battery_watchdog)
        self.battery_watchdog_thread.daemon = True
        self.battery_watchdog_thread.start()


    def vrfy(self, obj, params = {}):
        """
        コマンドのパラメータが正しいかどうかを確認する。
        """
        obj.pack_senddata(self.ad1, self.ad2, params)

    def run(self, obj, params = {}):
        """
        コマンドを送って/受信をする。
        エラーが発生したらリトライをする。
        リトライ回数はEssCommConfigで設定する
        """

        if 'number_of_dcdc_error_retry' in self.dcdc_config:
            retry = int(self.dcdc_config['number_of_dcdc_error_retry'])
        else:
            retry = 0
        if 'number_of_timeout_retry' in self.dcdc_config:
            timeout_retry = int(self.dcdc_config['number_of_timeout_retry'])
        else:
            timeout_retry = 0

        if 'wait_retry' in self.dcdc_config:
            wait_retry = int(self.dcdc_config['wait_retry'])
        else:
            wait_retry = 0.1

        retry += 1
        timeout_retry += 1
        while retry > 0 and timeout_retry > 0:
            try:
                obj.send(self.ad1, self.ad2, params)
                obj.recv()
                time.sleep(0.062)
                return
            except ESSXTimeoutException as err:
                print("timeout retry: " + str(timeout_retry))
                timeout_retry = timeout_retry - 1
                if timeout_retry == 0:
                    raise
                time.sleep(wait_retry)
            except ESSXValueException as err:
                print("value exception    retry: " + str(retry))
                retry = retry - 1
                if retry == 0:
                    raise
                time.sleep(wait_retry)
            except ESSXChecksumException as err:
                print("checksum exception    retry: " + str(retry))
                retry = retry - 1
                if retry == 0:
                    raise
                time.sleep(wait_retry)
            except ESSXDeviceException as err:
                _ercd = obj.response['ercd']
                if _ercd == 0xffff: #受信したコマンドをサポートしていない
                    raise
                elif _ercd == 0xfffe: #コマンドのパラメータが不正
                    raise
                elif _ercd == 0xfffd: #正常だが受付不能
                    pass
                elif _ercd == 0xfffc: #システムビジー
                    pass
                elif _ercd == 0xfffb: #内部通信異常
                    raise
                retry = retry - 1
                if retry == 0:
                    raise ESSXTimeoutException('timeout')
                time.sleep(wait_retry)
        #ここには到達しないはずだが保険
        raise ESSXTimeoutException('timeout')

    def _check_battery(self):
        if self.bat_dev == None:
            return (0, 0, -1)
        else:
            return self.bat_dev.check_battery()
#        rsoc = 0
#        bos = 0
#        comm_err1 = -1
#        comm_err2 = -1
#
#        retry = 2
#        while retry > 0 and self.bat_dev != None:
#            mes = self.bat_dev.read_rsoc()
#            #一分内のデータか？(この値が適切かは考慮するところ)
#            if mes != None and mes.timestamp > time.time() - 60:
#                rsoc = (mes.data[2] + mes.data[3] * 256) / 10.0
#                comm_err1 = 0
#                break
#            else:
#                self.bat_dev.remote_rsoc()
#                time.sleep(0.5)
#                retry = retry - 1
#                rsoc = 0
#
#        retry = 2
#        while retry > 0 and self.bat_dev != None:
#            mes = self.bat_dev.read_status()
#            #一分内のデータか？(この値が適切かは考慮するところ)
#            if mes != None and mes.timestamp > time.time() - 60:
#                if mes.data[0] == 1:
#                    bos = 3
#                else:
#                    bos = 0
#                comm_err2 = 0
#                break
#            else:
#                self.bat_dev.remote_status()
#                time.sleep(0.5)
#                retry = retry - 1
#                bos = 0
#
#        if comm_err1 == 0 and comm_err2 == 0:
#            comm_err = 0
#        else:
#            comm_err = -1
#        return (rsoc, bos, comm_err)

    def _battery_watchdog(self):
        """
        10秒に一回バッテリの融通チェックをする
        """
        essx_debug.log("watchdog start")
        while self.battery_watchdog_running:
            if self.bat_config == None:
                time.sleep(10)
                continue
            essx_debug.log("watchdog")
            essx_debug.log(self.bat_config['config'])
            if 'force_dcdc_waiting' in self.bat_config['config'] and self.bat_config['config']['force_dcdc_waiting'] == True:
                (rsoc, bos, comm_err) = self._check_battery()
                essx_debug.log("rsoc " +str(rsoc))
                essx_debug.log("bos " +str(bos))
                if bos == 0:
                    essx_debug.log("->waiting")
                    self.run(self.com0104, {
                        'mode': self.checkOperationMode_2500(0)
                    })
            time.sleep(10)
        essx_debug.log("watchdog stop")

    # /1/log/data
    def log_data(self, params):
        _dcdc_conf = self.dcdc_config['config']
        _bat_conf = self.bat_config['config']

        (rsoc, bos, comm_err) = self._check_battery()

        if comm_err == -1:
            raise ESSXDeviceException('comm error')
#        if bos == 0:
#            if 'force_dcdc_waiting' in self.bat_config['config'] and self.bat_config['config']['force_dcdc_waiting'] == True:
#                self.run(self.com0104, {
#                        'mode': self.checkOperationMode_2500(0)
#                })

        return {
            "system_time": {
                "year": 0,
                "month": 0,
                "day": 0,
                "hour": 0,
                "minute": 0
            },
            "rsoc": float(rsoc),
            "dischargeable_time": {
                "hour": 0,
                "minute": 0
            },
            "battery_voltage": _bat_conf['battery_voltage'],
            "battery_current": _bat_conf['battery_current'],
            "battery_rsoc" : float(rsoc),
            "battery_status": 0,
            "battery_warning": 0,
            #"battery_alarm": 0
            "battery_operation_status": bos,
            "battery_comm_err": comm_err,
            "charge_discharge_power": _bat_conf['battery_voltage'] * _bat_conf['battery_current'],
            "ups_operation_schedule": 0,
            "ups_operation_mode": {
                "mode": 0,
                "parameter": 0,
                "stop_mode": 0
            },
            #"ups_input_voltage": res["ac_input_voltage"],
            #"ups_output_voltage": res["ac_output_voltage"],
            #"ups_output_current": res["ac_output_current_r"] + res["ac_output_current_s"],
            #"ups_output_frequency": res["ac_output_frequency"],
            #"ups_output_power": res["ac_output_power"],
            #"pvc_charge_voltage": res["pvc_charge_voltage"],
            #"pvc_charge_current": res["pvc_charge_current"],
            #"pvc_charge_power": res["pvc_charge_power"],
            #"pvc_alarm": res["pvc_alarm"],
            #"ups_alarm": res["inverter_alarm"]
        }

    def remote_ioctl_set(self, params):
        _conf = self.dcdc_config['config']
        # EZA2500は 6-4で DIG, CIBを指定する必要がある。CIBは設定より得られるが
        # DIGは設定より得られないので、6-1で今の値を取得する
        self.run(self.com0601)
        self.run(self.com0604, {
                'ubv': _conf['ubv'],
                'ugv': _conf['ugv'],
                'obv': _conf['obv'],
                'ogv': _conf['ogv'],
                'cib': _conf['cib'],
                'dig': self.com0601.response['dig']
        })
        self.run(self.com0704, {
            'bcf': essx_util.strToNum(_conf['bcf']),
            'cvb': _conf['cvb'], #tvb => cvb
            'dlb': _conf['dlb'], #lbv => dlb
            'cdb': _conf['cdb'], #cud => cdb
            'ddb': _conf['ddb'], #dld => ddb
        })
        self.run(self.com0304, {
            'cvb': _conf['cvb'],
            'drb': _conf['drb'],
        })

        res = {}
        res["dcdc_converter_name"] = self.name
        #設定の戻値ではなく設定した値そのものを返す
        res["dcdc_setup_parameter"] = {
            "ubv": _conf['ubv'],
            "ugv": _conf['ugv'],
            "obv": _conf['obv'],
            "ogv": _conf['ogv'],
            "bcf": "0x" + ("%04x" % _conf['bcf']),
            "cvb": _conf['cvb'],
            "dlb": _conf['dlb'],
            "cdb": _conf['cdb'],
            "ddb": _conf['ddb'],
            "drb": _conf['drb'],
            "cib": _conf['cib'],
            #"lbd": _conf['lbd']
        }
        return res

    def remote_ioctl_get(self, params):
        _res_log_data = self.log_data(params)
        _res_remote_get = self.remote_get(params)
        return {
            "status": _res_remote_get["status"],
            "powermeter": _res_remote_get["powermeter"],
            "meter": _res_remote_get["meter"],
            "vdis": _res_remote_get["vdis"],
            "param": _res_remote_get["param"],
            "system_time": _res_log_data["system_time"],
            "rsoc": _res_log_data["rsoc"],
            "dischargeable_time": _res_log_data["dischargeable_time"],
            "battery_voltage": _res_log_data["battery_voltage"],
            "battery_current": _res_log_data["battery_current"],
            "battery_rsoc" : _res_log_data["battery_rsoc"],
            "battery_status": _res_log_data["battery_status"],
            "battery_warning": _res_log_data["battery_warning"],
            #"battery_alarm": 0
            "battery_operation_status": _res_log_data["battery_operation_status"],
            "battery_comm_err": _res_log_data["battery_comm_err"],
            "charge_discharge_power": _res_log_data["charge_discharge_power"],
            "ups_operation_schedule": _res_log_data["ups_operation_schedule"],
            "ups_operation_mode": _res_log_data["ups_operation_mode"],
            #"ups_input_voltage": res["ac_input_voltage"],
            #"ups_output_voltage": res["ac_output_voltage"],
            #"ups_output_current": res["ac_output_current_r"] + res["ac_output_current_s"],
            #"ups_output_frequency": res["ac_output_frequency"],
            #"ups_output_power": res["ac_output_power"],
            #"pvc_charge_voltage": res["pvc_charge_voltage"],
            #"pvc_charge_current": res["pvc_charge_current"],
            #"pvc_charge_power": res["pvc_charge_power"],
            #"pvc_alarm": res["pvc_alarm"],
            #"ups_alarm": res["inverter_alarm"]
        }

    def remote_get(self, params):
        _conf = self.dcdc_config['config']
        self.run(self.com0101, {})
        self.run(self.com0201, {})
        self.run(self.com0401, {})
        self.run(self.com0901, {})
        self.run(self.com1001, {})
        self.run(self.com0601, {})

        res = {}

        res["operationMode"] = self.com0101.response['mode']
        res["alarmState"] = (self.com0201.response['cst'] & 0xc) >> 2
        res["alarm"] = (self.com0901.response['alm1'])
        res["status"] =    (self.com0201.response['cst'] & 0x3)
        res["wg"] = round(self.com1001.response['wg'], 16)
        res["tmp"] = round(self.com1001.response['tmp'],16) 
        res["vb"] = round(self.com1001.response['vb'], 16)
        res["wb"] = round(self.com1001.response['wb'], 16)
        res["vg"] = round(self.com1001.response['vg'], 16)
        res["ib"] = round(self.com1001.response['ib'], 16)
        res["ig"] = round(self.com1001.response['ig'], 16)
        res["dvg"] = round(self.com0401.response['dvg'], 16)  #tgv => dvg
        res["drg"] = round(self.com0401.response['drg'], 16)
        res["dig"] = round(self.com0601.response['dig'], 16) #lgc, lgd => dig


        return {
            "status": {
                "status": "0x" + ('%04x' % res["operationMode"]),
                "alarmState": essx_util.alarmStateStr(res["alarmState"]),
                "statusName": "Ignore",
                "alarm": essx_util.alarmStr(res["status"], res["alarmState"], res["alarm"]),
                "runningState": essx_util.runningStateStr(res['status']),
                "operationMode": essx_util.operationModeStr(res["operationMode"]),
            },
            "powermeter": {
                "p2": 0,
                "p1": 0,
                "v1": 0,
                "kwh2": 0,
                "kwh1": 0,
                "i1": 0
            },
            "meter": {
                "wg": res["wg"],
                "tmp": res["tmp"],
                "vb": res["vb"],
                "wb": res["wb"],
                "vg": res["vg"],
                "ib": res["ib"],
                "ig": res["ig"]
            },
            "vdis": {
                "dvg": res["dvg"],
                "drg": res["drg"]
            },
            "param": {
                'cib': _conf['cib'],
                'ubv': _conf['ubv'],
                "dig": res["dig"],
                'ogv': _conf['ogv'],
                'obv': _conf['obv'],
                'ugv': _conf['ugv'],
            }
        }

    def remote_get_status(self, params):
        self.run(self.com0101)
        self.run(self.com0201)
        self.run(self.com1001)

        res = {}
        res["operationMode"] = self.com0101.response['mode']
        res["alarmState"] = (self.com0201.response['cst'] & 0xc) >> 2
        res["status"] =    (self.com0201.response['cst'] & 0x3)
        res["wg"] = round(self.com1001.response['wg'], 16)
        res["tmp"] = round(self.com1001.response['tmp'], 16)
        res["vb"] = round(self.com1001.response['vb'], 16)
        res["wb"] = round(self.com1001.response['wb'], 16)
        res["vg"] = round(self.com1001.response['vg'], 16)
        res["ib"] = round(self.com1001.response['ib'], 16)
        res["ig"] = round(self.com1001.response['ig'], 16)


        return {
            "status": {
                "alarmState": essx_util.alarmStateStr(res["alarmState"]),
                "runningState": essx_util.runningStateStr(res['status']),
                "operationMode": essx_util.operationModeStr(res["operationMode"]),
            },
            "meter": {
                "wg": res["wg"],
                "tmp": res["tmp"],
                "vb": res["vb"],
                "wb": res["wb"],
                "vg": res["vg"],
                "ib": res["ib"],
                "ig": res["ig"]
            },
        }

    def remote_set(self, params):
        """
        以下のパラメータが必要である
        params['mode']
        params['dvg']
        params['drg']
        params['dig']

        'mode'は16進数の文字列(EZA2500値)で指定する。
        読み変えはしない
        """
        _conf = self.dcdc_config['config']

        _local_current_mode = params['current_mode'] ##############Added for v1.3 2018/11/14##################

        self.vrfy(self.com0404, {
            'dvg': params['dvg'], 'drg': params['drg']
        })

        self.vrfy(self.com0604, {
            'ubv': _conf['ubv'],
            'ugv': _conf['ugv'],
            'obv': _conf['obv'],
            'ogv': _conf['ogv'],
            'cib': _conf['cib'],
            'dig': params['dig'],
        })
        self.vrfy(self.com0104, {
            'mode': self.checkOperationMode_2500(essx_util.hexStrToInt(params['mode']))
        })

        #############Added for v1.3 2018/11/14 現在のModeによってコマンドの発行順を変える。###################
        ##########################################↓↓↓#########################################################
        if _local_current_mode  == '0x0000':
             self.run(self.com0404, {
                 'dvg': params['dvg'], 'drg': params['drg']
             })
             self.run(self.com0604, {
                 'ubv': _conf['ubv'],
                 'ugv': _conf['ugv'],
                 'obv': _conf['obv'],
                 'ogv': _conf['ogv'],
                 'cib': _conf['cib'],
                 'dig': params['dig'],
             })
             self.run(self.com0104, {
                 'mode': self.checkOperationMode_2500(essx_util.hexStrToInt(params['mode']))
             })
        else:
             self.run(self.com0104, {
                 'mode': self.checkOperationMode_2500(essx_util.hexStrToInt(params['mode']))
             })
             self.run(self.com0404, {
                 'dvg': params['dvg'], 'drg': params['drg']
             })
             self.run(self.com0604, {
                 'ubv': _conf['ubv'],
                 'ugv': _conf['ugv'],
                 'obv': _conf['obv'],
                 'ogv': _conf['ogv'],
                 'cib': _conf['cib'],
                 'dig': params['dig'],
             })
        ##########################################↑↑↑######################################################
        ###################################################################################################
        
        self.run(self.com0201)
        self.run(self.com0901)
        self.run(self.com1001)

        res = {}
        res["operationMode"] = self.com0104.response['mode']
        res["alarmState"] = (self.com0201.response['cst'] & 0xc) >> 2
        res["alarm"] = (self.com0901.response['alm1'])
        res["status"] = (self.com0201.response['cst'] & 0x3)
        res["wg"] = round(self.com1001.response['wg'], 16)
        res["tmp"] = round(self.com1001.response['tmp'], 16)
        res["vb"] = round(self.com1001.response['vb'], 16)
        res["wb"] = round(self.com1001.response['wb'], 16)
        res["vg"] = round(self.com1001.response['vg'], 16)
        res["ib"] = round(self.com1001.response['ib'], 16)
        res["ig"] = round(self.com1001.response['ig'], 16)
        res["dvg"] = round(self.com0404.response['dvg'], 16)
        res["drg"] = round(self.com0404.response['drg'], 16)
        res["dig"] = round(self.com0604.response['dig'], 16)

        return {
            "status": {
                "status": "0x" + ('%04x' % res["operationMode"]),
                "alarmState": essx_util.alarmStateStr(res["alarmState"]),
                "statusName": "Ignore",
                "alarm": essx_util.alarmStr(res["status"], res["alarmState"], res["alarm"]),
                "runningState": essx_util.runningStateStr(res['status']),
                "operationMode": essx_util.operationModeStr(res["operationMode"]),
            },
            "meter": {
                "wg": res["wg"],
                "tmp": res["tmp"],
                "vb": res["vb"],
                "wb": res["wb"],
                "vg": res["vg"],
                "ib": res["ib"],
                "ig": res["ig"]
            },
            "vdis": {
                "dvg": res["dvg"],
                "drg": res["drg"]
            },
            "param": {
                'cib': _conf['cib'],
                'ubv': _conf['ubv'],
                "dig": res["dig"],
                'ogv': _conf['ogv'],
                'obv': _conf['obv'],
                'ugv': _conf['ugv'],
            }
        }

    def remote_set_current(self, params):
        """
        digが必須
        mode, dvg, drg も上層より指定されてくるが必須ではない。
        """
        _conf = self.dcdc_config['config']

        self.vrfy(self.com0604, {
            'ubv': _conf['ubv'],
            'ugv': _conf['ugv'],
            'obv': _conf['obv'],
            'ogv': _conf['ogv'],
            'cib': _conf['cib'],
            'dig': params['dig'],
        })

        self.run(self.com0604, {
            'ubv': _conf['ubv'],
            'ugv': _conf['ugv'],
            'obv': _conf['obv'],
            'ogv': _conf['ogv'],
            'cib': _conf['cib'],
            'dig': params['dig'],
        })
        self.run(self.com1001)
        res = {}
        res["wg"] = round(self.com1001.response['wg'], 16)
        res["tmp"] = round(self.com1001.response['tmp'], 16)
        res["vb"] = round(self.com1001.response['vb'], 16)
        res["wb"] = round(self.com1001.response['wb'], 16)
        res["vg"] = round(self.com1001.response['vg'], 16)
        res["ib"] = round(self.com1001.response['ib'], 16)
        res["ig"] = round(self.com1001.response['ig'], 16)
        res["dig"] = round(self.com0604.response['dig'], 16)

        return {
            "meter": {
                "wg": res["wg"],
                "tmp": res["tmp"],
                "vb": res["vb"],
                "wb": res["wb"],
                "vg": res["vg"],
                "ib": res["ib"],
                "ig": res["ig"]
            },
            "param": {
                'cib': _conf['cib'],
                'ubv': _conf['ubv'],
                "dig": res["dig"],
                'ogv': _conf['ogv'],
                'obv': _conf['obv'],
                'ugv': _conf['ugv'],
            }
        }


    def remote_set_voltage(self, params):
        """
        dvg, drg が必須
        mode, digも上層より指定されてくるが必須ではない。
        """
        self.vrfy(self.com0404, {
            'dvg': params['dvg'], 'drg': params['drg']
        })
        self.run(self.com0404, {
            'dvg': params['dvg'], 'drg': params['drg']
        })

        self.run(self.com1001)
        res = {}
        res["wg"] = round(self.com1001.response['wg'], 16)
        res["tmp"] = round(self.com1001.response['tmp'], 16)
        res["vb"] = round(self.com1001.response['vb'], 16)
        res["wb"] = round(self.com1001.response['wb'], 16)
        res["vg"] = round(self.com1001.response['vg'], 16)
        res["ib"] = round(self.com1001.response['ib'], 16)
        res["ig"] = round(self.com1001.response['ig'], 16)
        res["dvg"] = round(self.com0404.response['dvg'], 16)
        res["drg"] = round(self.com0404.response['drg'], 16)

        return {
            "meter": {
                "wg": res["wg"],
                "tmp": res["tmp"],
                "vb": res["vb"],
                "wb": res["wb"],
                "vg": res["vg"],
                "ib": res["ib"],
                "ig": res["ig"]
            },
            "vdis": {
                "dvg": res["dvg"],
                "drg": res["drg"]
            },
        }

    def remote_ioctl_clr_alarm(self, params):
        self.run(self.com0904, {
            'd0': 0, 'd1': 0,
        })
        return self.remote_get(params)

    def checkOperationMode_2500(self, v):
        """
        EZA2500のoperationModeの確認
        """
        if v == 0x0:
            return v
        elif v == 0x2:
            return v
        elif v == 0x14:
            return v
        elif v == 0x41:
            return v
        raise ESSXValueException("bad value: operation mode: " + str(v))

if __name__    == "__main__":
    #単体テストのためにはデバイスの先に装置(esscomm/ess2/test.py で代用可)が必要
    import serial
    import traceback
    import essx

    ser_dev = essx.essx_rs232c.ESSXRS232C("/dev/cuaU1", 19200)
    dcdc_dev = eza2500.EZA2500Device(dev = ser_dev, timeout = 1)
    controller = ESSXType3(dcdc_dev = dcdc_dev, bat_dev = None, dcdc_config = {
        'config': {
            'bcf': 0,
#            'lbc': 45.8, #バッテリ上限電流(充電)
#            'lbd': 45.8, #バッテリ上限電流(放電)
            'ubv': 60,  #バッテリ低電圧閾値
            'obv': 60,  #バッテリ過電圧閾値
            #'lgc': 34.4, #グリッド上限電流(充電)
            #'lgd': 34.4, #グリッド上限電流(放電)
            'ugv': 280,  #グリッド低電圧閾値
            'ogv': 280,  #グリッド過電圧閾値
            #'tgv': 240,  #グリッド目標電圧
            'cvb': 40,  #バッテリ目標電圧
            'dlb': 40,  #バッテリ放電終止電圧
            'cdb': 6,   #バッテリ充電上限予告電圧偏差
            'ddb': 6,   #バッテリ充電終止予告電圧偏差
            #'drg': 0.25, #グリッドドループ率
            'drb': 0.25, #バッテリドループ率
            'cib': 45.8,
        }}, bat_config = {
            'config': {
                'battery_voltage': 200,
                'battery_current': 2.0
            }}
    )

    try:
        print("/1/log/data")
        print(controller.log_data({}))
        print("remote/ioctl/get")
        print(controller.remote_ioctl_get({}))
        print("remote/ioctl/set")
        print(controller.remote_ioctl_set({}))
        print(controller.remote_get({}))
        print(controller.remote_get_status({}))
        print(controller.remote_set({'mode': '0x02', 'dvg': 300, 'drg': 0.1, 'dig': 0}))
        print(controller.remote_set_current({'dig': 3}))
        print(controller.remote_set_voltage({'dvg': 360, 'drg': 0.2}))
        print(controller.remote_ioctl_clr_alarm({}))
    except ESSXValueException as err:
        print(err.reason)
        print(traceback.format_exc())
        raise err

# -*- coding: utf-8 -*- Rest APIとデバイスのやりとりをするコントローラクラス コマンドのパラメータが正しいかどうかを確認する。 コマンドを送って/受信をする。         エラーが発生したらリトライをする。         リトライ回数はEssCommConfigで設定する #受信したコマンドをサポートしていない #コマンドのパラメータが不正 #正常だが受付不能 #システムビジー #内部通信異常 #ここには到達しないはずだが保険 #        rsoc = 0 #        bos = 0 #        comm_err1 = -1 #        comm_err2 = -1 # #        retry = 2 #        while retry > 0 and self.bat_dev != None: #            mes = self.bat_dev.read_rsoc() #            #一分内のデータか？(この値が適切かは考慮するところ) #            if mes != None and mes.timestamp > time.time() - 60: #                rsoc = (mes.data[2] + mes.data[3] * 256) / 10.0 #                comm_err1 = 0 #                break #            else: #                self.bat_dev.remote_rsoc() #                time.sleep(0.5) #                retry = retry - 1 #                rsoc = 0 # #        retry = 2 #        while retry > 0 and self.bat_dev != None: #            mes = self.bat_dev.read_status() #            #一分内のデータか？(この値が適切かは考慮するところ) #            if mes != None and mes.timestamp > time.time() - 60: #                if mes.data[0] == 1: #                    bos = 3 #                else: #                    bos = 0 #                comm_err2 = 0 #                break #            else: #                self.bat_dev.remote_status() #                time.sleep(0.5) #                retry = retry - 1 #                bos = 0 # #        if comm_err1 == 0 and comm_err2 == 0: #            comm_err = 0 #        else: #            comm_err = -1 #        return (rsoc, bos, comm_err) 10秒に一回バッテリの融通チェックをする # /1/log/data #        if bos == 0: #            if 'force_dcdc_waiting' in self.bat_config['config'] and self.bat_config['config']['force_dcdc_waiting'] == True: #                self.run(self.com0104, { #                        'mode': self.checkOperationMode_2500(0) #                }) #"battery_alarm": 0 #"ups_input_voltage": res["ac_input_voltage"], #"ups_output_voltage": res["ac_output_voltage"], #"ups_output_current": res["ac_output_current_r"] + res["ac_output_current_s"], #"ups_output_frequency": res["ac_output_frequency"], #"ups_output_power": res["ac_output_power"], #"pvc_charge_voltage": res["pvc_charge_voltage"], #"pvc_charge_current": res["pvc_charge_current"], #"pvc_charge_power": res["pvc_charge_power"], #"pvc_alarm": res["pvc_alarm"], #"ups_alarm": res["inverter_alarm"] # EZA2500は 6-4で DIG, CIBを指定する必要がある。CIBは設定より得られるが # DIGは設定より得られないので、6-1で今の値を取得する #tvb => cvb #lbv => dlb #cud => cdb #dld => ddb #設定の戻値ではなく設定した値そのものを返す #"lbd": _conf['lbd'] #"battery_alarm": 0 #"ups_input_voltage": res["ac_input_voltage"], #"ups_output_voltage": res["ac_output_voltage"], #"ups_output_current": res["ac_output_current_r"] + res["ac_output_current_s"], #"ups_output_frequency": res["ac_output_frequency"], #"ups_output_power": res["ac_output_power"], #"pvc_charge_voltage": res["pvc_charge_voltage"], #"pvc_charge_current": res["pvc_charge_current"], #"pvc_charge_power": res["pvc_charge_power"], #"pvc_alarm": res["pvc_alarm"], #"ups_alarm": res["inverter_alarm"] #tgv => dvg #lgc, lgd => dig 以下のパラメータが必要である         params['mode']         params['dvg']         params['drg']         params['dig']          'mode'は16進数の文字列(EZA2500値)で指定する。         読み変えはしない ##############Added for v1.3 2018/11/14################## #############Added for v1.3 2018/11/14 現在のModeによってコマンドの発行順を変える。################### ##########################################↓↓↓######################################################### ##########################################↑↑↑###################################################### ################################################################################################### digが必須         mode, dvg, drg も上層より指定されてくるが必須ではない。 dvg, drg が必須         mode, digも上層より指定されてくるが必須ではない。 EZA2500のoperationModeの確認 #単体テストのためにはデバイスの先に装置(esscomm/ess2/test.py で代用可)が必要 #            'lbc': 45.8, #バッテリ上限電流(充電) #            'lbd': 45.8, #バッテリ上限電流(放電) #バッテリ低電圧閾値 #バッテリ過電圧閾値 #'lgc': 34.4, #グリッド上限電流(充電) #'lgd': 34.4, #グリッド上限電流(放電) #グリッド低電圧閾値 #グリッド過電圧閾値 #'tgv': 240,  #グリッド目標電圧 #バッテリ目標電圧 #バッテリ放電終止電圧 #バッテリ充電上限予告電圧偏差 #バッテリ充電終止予告電圧偏差 #'drg': 0.25, #グリッドドループ率 #バッテリドループ率