package ch.hevs.isi.field;

import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.ip.tcp.TcpMaster;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.sun.org.apache.xpath.internal.operations.Mod;

public class ModbusAccessor {

    private static ModbusAccessor mySelf = null;        //instance of the class
    private ModbusMaster master = null;                 //instance of the modbus master

    /**
     * private constructor
     * */
    private ModbusAccessor(){
    }

    /**
     * static method to create a singleton pattern of the class
     * checks if an instance of the class is already made
     * if not, it creates an instance of the class ModbusAccessor
     * @return instance of ModbusAccessor
     */
    public static ModbusAccessor getMySelf(){
        if (mySelf == null){                    //if no instance is created
            mySelf = new ModbusAccessor();      //create a new instance of the class
        }
        return mySelf;                          //if there is already an instance, return the existing instance
    }

    /**
     *  this method creates a ModbusFactory and a TCPMaster
     *  it also initialize the Master
     * @param ipAddress IP-address of the server
     * @param port port of the server
     */
    public void connect(String ipAddress, int port){
        ModbusFactory MF = new ModbusFactory();         //create a modbus factory
        IpParameters IpParams = new IpParameters();     //create the ip parameters

        IpParams.setHost(ipAddress);        //set the server ip-address
        IpParams.setPort(port);             //set the server port

        //create the TCPMaster with the correct IP parameters and keep it alive
        this.master = MF.createTcpMaster(IpParams, true);
        try {
            this.master.init();         //initialize the master
        }
        catch(ModbusInitException e)
        {
            e.printStackTrace();
        }
    }
    //methods to write
    /**
     * method to write a boolean value in the correct modbus register
     * @param register address of the register
     * @param value the desired boolean value
     */
    public void writeBoolean (int register, boolean value){
        //first parameter = slaveID = always 1 because we only have one
        //second parameter = register address ==> it is named offset because it starts with 0 and goes to the desired address
       try{
           this.master.setValue(BaseLocator.coilStatus(1,register), value); //set the desired value in the correct register
       }
       catch (ModbusTransportException e){
           e.printStackTrace();
       }
       catch (ErrorResponseException e){
           e.printStackTrace();
       }
    }

    /**
     * method to write a float value in the correct modbus register
     * @param register address of the register
     * @param value the desired float value
     */
    public void writeFloat (int register, float value){
        //first parameter = slaveID = always 1 because we only have one
        //second parameter = register address ==> it is named offset because it starts with 0 and goes to the desired address
        //third parameter = DataType of the value. The max value is 3 Byte Float, but the class DataType has only 2 or 4 byte
        try{
            this.master.setValue(BaseLocator.holdingRegister(1,register,DataType.FOUR_BYTE_FLOAT), value);
        }
        catch (ModbusTransportException e){
            e.printStackTrace();
        }
        catch (ErrorResponseException e){
            e.printStackTrace();
        }
    }
    //methods to read
    /**
     * method to read a boolean value in the correct modbus register
     * get the coil status of the register.
     * @param register address of register
     * @return boolean value of the desired register
     */
    public boolean readBoolean(int register){
        //first parameter = slaveID = always 1 because we only have one
        //second parameter = register address ==> it is named offset because it starts with 0 and goes to the desired address
        try{
            boolean booleanValue = this.master.getValue(BaseLocator.coilStatus(1,register));
            return booleanValue;
        }
        catch (ModbusTransportException e){
            e.printStackTrace();
        }
        catch (ErrorResponseException e){
            e.printStackTrace();
        }
        return false;
    }
    /**
     * method to read a boolean value in the correct modbus register
     * get the value from the holding register
     * @param register address of the register
     * @return float value of the desired register
     */
    public float readFloat(int register){
        //first parameter = slaveID = always 1 because we only have one
        //second parameter = register address ==> it is named offset because it starts with 0 and goes to the desired address
        //third parameter = DataType of the value. The max value is 3 Byte Float, but the class DataType has only 2 or 4 byte
        try {
            float floatValue = (float) this.master.getValue(BaseLocator.holdingRegister(1,register, DataType.FOUR_BYTE_FLOAT));
            return floatValue;
        }
        catch (ModbusTransportException e){
            e.printStackTrace();
        }
        catch (ErrorResponseException e){
            e.printStackTrace();
        }
        return 0F;
    }
    /**
     * this main method is only for testing the ModbusAccessor class
     * @param args
     */
    /*
    public static void main(String[] args) {
        //create an instance of ModbusAccessor and connect it with the server
        ModbusAccessor test = ModbusAccessor.getMySelf();
        test.connect("LocalHost", 1502);
        //do this all the time
        while(true){
                //get a boolean value => solar SetPoint
                boolean solar_connect_st = test.readBoolean(609);           //SOLAR_CONNECT_ST
                //get a float value => factory SetPoint
                float factory_st = test.readFloat(605);                     //FACTORY_ST

                //write a float value to the Remote_Factory_SP
                test.writeFloat(205,0.56F);                         //REMOTE_FACTORY_SP
                //write a boolean value to the Remote_Solar_SW
                test.writeBoolean(401,false);                       //REMOTE_SOLAR_SW

                //check the factory SetPoint
                System.out.println("Factory SetPoint is: " + factory_st);

                //check the solar SetPoint
                if(solar_connect_st){
                    System.out.println("Solar is connected");
                }else{
                    System.out.println("Solar is disconnected");
                }
        }
    }
     */
}




