/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.openremote.controller.protocol.ehouse;
import java.io.*;
import java.net.*;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.lang.InterruptedException;
        
/**
 *
 * Non blocking TCP server (multi thread) for receiving statuses from eHouse system
 * @author robert jarzabek 
 * http://www.isys.pl/ 
 * http://inteligentny-dom.ehouse.pro/
 * http://www.ehouse.pro/
 * http://sterowanie.biz/
 * 
 * 
 * eHouse protocol for Open Remote
 * query command via tcp/ip ascii string hex representation of value eg. 00fe0102 case insensitive
 * aabbccdd    - query command for eHouse Java Server
 * aa - eHouse device high address 00-FF
 * bb - ehouse device low address 00-FF
 * cc - sensor query code for object in Open remote (***) - signal type
 * dd - Item Number /signal nr (i / o / adc eg.)
 * 
 * server connection is kept for each sensor
 * return state of signal capture to eHouse java sever cache
 * report state depending on signal type on/off for digital inputs, outputs,
 * string representation for analog and numerical values 
 * 
 * in case of changing signal state of controller automatically return state to socket connection while connection is active
 * 
 * some signal type can be change directly from socket sending command on, off, toggle,0, 1 or number for numeric signal (dimmers, programs)
 * 
 * 
 * 
 * aabbccddeeffgghhiijj - sending direct events to controller via eHouse Java Server
 * aa - eHouse device high address 00-FF
 * bb - ehouse device low address 00-FF
 * cc - event code 00-ff
 * dd - arg1 for event 00-ff
 * ee - arg2 for event 00-ff
 * ff - arg3 for event 00-ff
 * gg - arg4 for event 00-ff
 * hh - arg5 for event 00-ff
 * ii - arg6 for event 00-ff
 * jj - arg7 for event 00-ff
 * 
 */
public class StatusServer extends Thread {
    /*
     * Sensor query code (***)  commands
     * 
     */
    final static int SIGNAL_OUTPUT_STATE=1;         //query output state
    final static int SIGNAL_INPUT_STATE=2;          //query input state
    final static int SIGNAL_DIMMER_LEVEL=3;         //dimmer level
    final static int SIGNAL_CURRENT_PROGRAM=4;      //current program
    final static int SIGNAL_ADC_VALUE=5;            //Analog to digital converter value <0..1023>
    final static int SIGNAL_LM335_VALUE=6;          //LM335 sensor Temperature calculated value
    final static int SIGNAL_VOLTAGE_VALUE=7;        //voltage value
    final static int SIGNAL_PERCENT_VALUE=8;        //percent value in reference to power supply
    final static int SIGNAL_INVPERCENT_VALUE=9;     //inverted percent value 100-x %
    final static int SIGNAL_MCP9700_VALUE=10;       //MCP9700 temperature sensor calculated value
    final static int SIGNAL_MCP9701_VALUE=11;       //MCP9701 temperature sensor calculated value
    final static int SIGNAL_WHOLE_STATUS=12;        //whole binary (converted to ascii string) status from controller

    
    
    
    
        int currentconection=0;
        String DirectEventTemplate="";              //template of event for current signal query
	String message;
//	StatusServer(){}
        Socket newsock=null;                        //socket connection
        boolean Changed=false;                      //changed flag
        int CurrentInstance;
        int EthernetDevIndex=-1;                    //Ethernet Device index (StatusEthernet) of EhDevTCP[]
        int Ehouse1DevIndex=-1;                     //eHouse 1 Device index (StatusEhouse) of EhDev[]
        int NrItem=-1;                              //Signal Number of Item
        int SignalType=-1;                          //Signal type for query from open remote
        boolean StateBool=false;                    //Boolean state of sensor
        int State=0;                                //integer state of sensor
        double StateADC=0;                          //double state of sensor
        
StatusServer(Socket s,int v)                        // initializing the variables of new TCP server instance for sensor
{                           
    this.newsock = s;
    this.CurrentInstance = v;    
    this.EthernetDevIndex=-1;
    this.Ehouse1DevIndex=-1;
    this.NrItem=-1;
    this.SignalType=-1;
    this.DirectEventTemplate="";
    this.State=0;
    this.StateADC=0;
    this.StateBool=false;
}        
//////////////////////////////////////////////////////////////////////////////////
/**
 * Submiting event via tcp directly to controller 
 * @param EventName - name of event for logging
 * @param DirectEvent - direct event command to controller
 */
void suEvenet(String EventName,String DirectEvent)                  // 
    {
    Byte[] schedule=new Byte[3];
    schedule[0]=0;
    schedule[1]=0;
    schedule[2]=0;
    ehousecommunication.AddAsciiEvent(DirectEvent);                 //Add event to sending queue
    int adrh=ehousecommunication.GetHexU(DirectEvent,0);
    if ((adrh>0) && (adrh<86))                                      //if adress h (1,86) Old RoomManagers (eHouse1)
                        ehousecommunication.SendWiFi("", 0);        // sends via commmanager or eHouse PC server
    else                                                            //adrh =0 or >85 Ethernet Rooom Manager etc.
        {                                                           //sends directly to dedicated controller
        int adrl=ehousecommunication.GetHexU(DirectEvent,2);
        ehousecommunication.SendWiFi("192.168."+String.valueOf(adrh)+"."+String.valueOf(adrl), 0);
        }
    }
////////////////////////////////////////////////////////////////////////////////        
/**
 * Checking index of eHouse 1 (eHDev) and ethernet ehouse (eHDevTCP) controller from internal cache (
 * @param devh - high address
 * @param devl - low address
 * sets global varibles EthernetDevIndex or Ehouse1DevIndex depending of controller type
 */
        void CheckIndex(int devh,int devl)
        {
        EthernetDevIndex=-1;
        Ehouse1DevIndex=-1;
        for (int i = 0; i < EhouseTCP.RM_COUNT; i++)  
            {
            if (EhouseTCP.EhDev[i]!=null)
                if (EhouseTCP.EhDev[i].isAddress(devh, devl))
                    {
                        Ehouse1DevIndex=i;
                        return;
                    }
            }
        for (int i = 0; i < EhouseTCP.TCP_COUNT; i++)  
            {
            if (EhouseTCP.EhDevTCP[i]!=null)
                if (EhouseTCP.EhDevTCP[i].isAddress(devh, devl))
                    {
                        EthernetDevIndex=i;
                        return;
                    }
            }
        }
        
        /////////////////////////////////////////////////////////////////////////////////////
        /**
         * Get ehouse 1 devices states and values for open remote and assigning EventTemplate
         * @return String = state 
         */
        String GetEhouse1State()
        {
        if (Ehouse1DevIndex<0) return ""; //only for  ehouse1 devs otherwise exit
        int prevstate=State;    
        boolean prevstatebool=StateBool;
        double prevstatedouble=StateADC;
            int ind=Ehouse1DevIndex;
            switch (SignalType)    
            {
                case SIGNAL_OUTPUT_STATE: //output state
                    {
                    StateBool=EhouseTCP.EhDev[ind].OutputStates[NrItem];
                    if (prevstatebool!=StateBool) Changed=true;
                    DirectEventTemplate=ehousecommunication.ConvertAsciHex(EhouseTCP.EhDev[ind].DevAdrH)
                                    +ehousecommunication.ConvertAsciHex(EhouseTCP.EhDev[ind].DevAdrL)
                                    +"01"
                                    +ehousecommunication.ConvertAsciHex(NrItem+1)
                                    +"%%0000000000";
                    if (StateBool) return "On";
                    else return "Off";
                    }
                case SIGNAL_INPUT_STATE:  //input state
                    {
                    StateBool=EhouseTCP.EhDev[ind].InputStates[NrItem];
                    if (prevstatebool!=StateBool) Changed=true;
                    if (StateBool) return "On";
                    else return "Off";
                    }
                case SIGNAL_DIMMER_LEVEL:     //dimmer level
                    {
                    State=EhouseTCP.EhDev[ind].Dimmer[NrItem];
                    if (prevstate!=State) Changed=true;                
                    return String.valueOf(State);
                    }
                case SIGNAL_CURRENT_PROGRAM:    //current program
                    {
                    State=EhouseTCP.EhDev[ind].CurrentProgram;
                    if (prevstate!=State) Changed=true;
                    if (!EhouseTCP.EhDev[ind].IsHM)
                    DirectEventTemplate=ehousecommunication.ConvertAsciHex(EhouseTCP.EhDev[ind].DevAdrH)
                                    +ehousecommunication.ConvertAsciHex(EhouseTCP.EhDev[ind].DevAdrL)
                                    +"02"
                                    +ehousecommunication.ConvertAsciHex(NrItem+1)
                                    +"%%0000000000";
                    else 
                        DirectEventTemplate=ehousecommunication.ConvertAsciHex(EhouseTCP.EhDev[ind].DevAdrH)
                                    +ehousecommunication.ConvertAsciHex(EhouseTCP.EhDev[ind].DevAdrL)
                                    +"AA"
                                    +ehousecommunication.ConvertAsciHex(NrItem+1)
                                    +"%%0000000000";
                    return String.valueOf(State);
                    }
                case SIGNAL_ADC_VALUE:   //ADC value 0..1023
                    {
                    StateADC=EhouseTCP.EhDev[ind].SensorABSValues[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }

                case SIGNAL_LM335_VALUE:   //ADC LM335 Value
                    {
                    StateADC=EhouseTCP.EhDev[ind].SensorTemps[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
                case SIGNAL_VOLTAGE_VALUE:  //ADC Voltage value
                    {
                    StateADC=EhouseTCP.EhDev[ind].SensorVolts[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
                case SIGNAL_PERCENT_VALUE:  //ADC percent value
                    {
                    StateADC=EhouseTCP.EhDev[ind].SensorPercents[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
                case SIGNAL_INVPERCENT_VALUE: //ADC InvPercent value
                    {
                    StateADC=EhouseTCP.EhDev[ind].SensorLights[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
/*        case SIGNAL_MCP9700_VALUE:  //mcp9700 value
                    {
                    StateADC=EhouseTCP.EhDev[Ehouse1DevIndex].SensorLights[MCP9700State];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
    
    
     case SIGNAL_MCP9701_VALUE:       //mcp9701 value
                    {
                    StateADC=EhouseTCP.EhDev[Ehouse1DevIndex].SensorLights[MCP9701State];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }*/
    case SIGNAL_WHOLE_STATUS:       //whole status
                    {
                  
                    return ehousecommunication.hib(EhouseTCP.EhDev[ind].CurrentStatus,EhouseTCP.EhDev[ind].STATUS_SIZE);
                    }                    
            }
       
    return "-";    
        }
/////////////////////////////////////////////////////////////////////////////////        
        /**
         * Get Ethernet eHouse devices states and values for open remote and assigning EventTemplate
         * @return String = state 
         */
String GetEthernetEhouseState()
        {
        if (EthernetDevIndex<0) return "";      //only for ethernet ehouse devices otherwise exit
        int prevstate=State;    
        boolean prevstatebool=StateBool;
        double prevstatedouble=StateADC;
            int ind=EthernetDevIndex;
            switch (SignalType)    
            {
                case SIGNAL_OUTPUT_STATE: //output state
                {
                StateBool=EhouseTCP.EhDevTCP[ind].OutputStates[NrItem];
                if (prevstatebool!=StateBool) Changed=true;
                DirectEventTemplate=ehousecommunication.ConvertAsciHex(EhouseTCP.EhDevTCP[ind].DevAdrH)
                                    +ehousecommunication.ConvertAsciHex(EhouseTCP.EhDevTCP[ind].DevAdrL)
                                    +"21"
                                    +ehousecommunication.ConvertAsciHex(NrItem)
                                    +"%%0000000000";
                    
                if (StateBool) return "On";
                else return "Off";
                }
                case SIGNAL_INPUT_STATE:  //input state
                {
                StateBool=EhouseTCP.EhDevTCP[ind].InputStates[NrItem];
                if (prevstatebool!=StateBool) Changed=true;
                if (StateBool) return "On";
                else return "Off";
                }
                case SIGNAL_DIMMER_LEVEL:     //dimmer level
                    {
                    State=EhouseTCP.EhDevTCP[ind].Dimmer[NrItem];
                    if (prevstate!=State) Changed=true;                
                    return String.valueOf(State);
                    }
                case SIGNAL_CURRENT_PROGRAM:    //current program
                    {
                    State=EhouseTCP.EhDevTCP[ind].CurrentProgram;
                    if (prevstate!=State) Changed=true;
                    if (!EhouseTCP.EhDevTCP[ind].IsEHM)
                    DirectEventTemplate=ehousecommunication.ConvertAsciHex(EhouseTCP.EhDevTCP[ind].DevAdrH)
                                        +ehousecommunication.ConvertAsciHex(EhouseTCP.EhDevTCP[ind].DevAdrL)
                                        +"02"
                                    +ehousecommunication.ConvertAsciHex(NrItem)
                                    +"%%0000000000";
                    else 
                        DirectEventTemplate=ehousecommunication.ConvertAsciHex(EhouseTCP.EhDevTCP[ind].DevAdrH)
                                    +ehousecommunication.ConvertAsciHex(EhouseTCP.EhDevTCP[ind].DevAdrL)
                                    +"AA"
                                    +ehousecommunication.ConvertAsciHex(NrItem)
                                    +"%%0000000000";
                    return String.valueOf(State);
                    }
                case SIGNAL_ADC_VALUE:   //ADC value 0..1023
                    {
                    StateADC=EhouseTCP.EhDevTCP[ind].SensorABSValues[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }

                case SIGNAL_LM335_VALUE:   //ADC LM335 Value
                    {
                    StateADC=EhouseTCP.EhDevTCP[ind].SensorTemps[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
                case SIGNAL_VOLTAGE_VALUE:  //ADC Voltage value
                    {
                    StateADC=EhouseTCP.EhDevTCP[ind].SensorVolts[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
                case SIGNAL_PERCENT_VALUE:  //ADC percent value
                    {
                    StateADC=EhouseTCP.EhDevTCP[ind].SensorPercents[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
                case SIGNAL_INVPERCENT_VALUE: //ADC InvPercent value
                    {
                    StateADC=EhouseTCP.EhDevTCP[ind].SensorLights[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
        case SIGNAL_MCP9700_VALUE:  //mcp9700 value
                    {
                    StateADC=EhouseTCP.EhDevTCP[ind].SensorTempsMCP9700[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
    
    
     case SIGNAL_MCP9701_VALUE:       //mcp9701 value
                    {
                    StateADC=EhouseTCP.EhDevTCP[ind].SensorTempsMCP9701[NrItem];
                    if (prevstatedouble!=StateADC) Changed=true;
                    return String.valueOf(StateADC);
                    }
    case SIGNAL_WHOLE_STATUS:       //whole status
                    {
                  
                    return ehousecommunication.hib(EhouseTCP.EhDevTCP[ind].CurrentStatus,EhouseTCP.EhDev[ind].STATUS_SIZE);
                    }                    
    case 13:
                    {
                    return  EhouseTCP.EhDevTCP[ind].MakeXml();
                    }
            }
    return "-";    
        }        
/**
 * Read socket of current tcp/ip connection
 * @param ds - Input Stream
 * @return string value received
 * if nothing received return ""
 * if error return null
 */


  String ReadSock(InputStream ds)
        {
         char[] buffc=new char[255];
         int siz=0;
         
         try
                    {
                        
                    int retries=100;
                    while (ds.available()==0)
                                {
                                if (retries==0) break;    
                                retries--;
                                try
                                    {    
                                    this.sleep(10);    
                                    }
                                catch (java.lang.InterruptedException e)
                                    {
                                    break;
                                    }
                                
                                }
                               while (ds.available()>0)
                                    {
                                    int ch=ds.read();
                                    if (ch<0) return null;    
                                    buffc[siz]=(char) ch;
                                    siz++;
                                    }
                               
                     if (siz>0)                   
                         {
                               System.out.println( String.valueOf(buffc,0,siz));
                                return String.valueOf(buffc,0,siz);
                         }
                                
                    }
                 catch (IOException e )
                         {
                             System.out.print("Error Reading Message: "+e.getMessage());
                             return null;
                         }
                 
        
        return "";
        }
  
        //////////////////////////////////////////////////////////////////////////////////////////
	@Override public void run()             //Server connection 
	{
            System.out.println("Start Server Instance " + " #"+String.valueOf(CurrentInstance));
            InputStream ins=null;
            OutputStream outs=null;
            	try
                    {
                        newsock.setSoLinger(false,0);           ///set socket parameters should be optimized for best results
                        newsock.setSoTimeout(10000);
                        newsock.setKeepAlive(true);
                        ins=newsock.getInputStream();           //input stream of socket
                        outs=newsock.getOutputStream();         //output stream of socket
			outs.flush();
                        NrItem=-1;                              //reset sensor values
                        SignalType=-1;
                        State=0;
                        StateADC=0;
                        do                                      //continues loop
                         {
                         try
                                {
                                message=null;    
                                message=ReadSock(ins);          //read string value between timeouts
                                if (message!=null)
                                   {
                                   message=message.replaceAll(" ","");          //ignore spaces for easier implementation in open remote 
                                   if ((message.length()>7) && (message.length()<20))   //query command for checking devices signal states
                                                {
                                                
                                                message=message.toUpperCase();
                                                int devadrh=ehousecommunication.GetHexU(message,0);         //decode hex address high
                                                int devadrl=ehousecommunication.GetHexU(message, 2);        //decode hex address low
                                                CheckIndex(devadrh,devadrl);                                //get indexes of contollers from cache
                                                SignalType=ehousecommunication.GetHexU(message,4);          //get signal type for query
                                                NrItem=ehousecommunication.GetHexU(message, 6)-1;           //get Nr of this signal type
                                                Changed=true;                                               //Set modified flag
                                                System.out.println("Message : "+message);
                                                String resEth=GetEthernetEhouseState();                     //result string for Ethernet eHouse controller current signal State
                                                String resEhouse1=GetEhouse1State();                        //result string for eHouse1 controller current signal State
                                                if (resEth.length()>0) outs.write(resEth.getBytes());       
                                                if (resEhouse1.length()>0) outs.write(resEhouse1.getBytes());   //send signal state of signal
                                                Changed=false;                                                  //reset modification flag
                                                }
                                        else 
                                                if (message.length()==20)   //      Direct event to submit
                                                    {   
                                                    
                                                    message=message.toUpperCase();
                                                    suEvenet(message,message);      //submit direct event to ehouse controllers
                                                    }
                                                else //other issues
                                                    if (message.length()>0)         //received other data => directly change state of signal
                                                    {
                                                    String Event="";
                                                    if (SignalType==SIGNAL_OUTPUT_STATE)        //for outputs
                                                        {
                                                        boolean enventtorun=false;    
                                                        if (message.toLowerCase().equals("on")) 
                                                        
                                                                    {
                                                                        Event=DirectEventTemplate.replaceAll("%%", "01");   //get template of event for current signal
                                                                        enventtorun=true;
                                                                    }
                                                        if (message.toLowerCase().equals("off")) {Event=DirectEventTemplate.replaceAll("%%", "00");enventtorun=true;}
                                                        if (message.toLowerCase().equals("toggle")) {Event=DirectEventTemplate.replaceAll("%%", "02");enventtorun=true;}
                                                        if (message.toLowerCase().equals("sw")) {Event=DirectEventTemplate.replaceAll("%%", "02");enventtorun=true;}
                                                        if (message.toLowerCase().equals("1")) {Event=DirectEventTemplate.replaceAll("%%", "01");enventtorun=true;}
                                                        if (message.toLowerCase().equals("0")) {Event=DirectEventTemplate.replaceAll("%%", "00");enventtorun=true;}
                                                        if (message.toLowerCase().equals("2")) {Event=DirectEventTemplate.replaceAll("%%", "02");enventtorun=true;}
                                                        if (enventtorun)                        //if match items
                                                            {
                                                            suEvenet(Event,Event);              //submit direct event
                                                            }
                                                        }
                                                    }
                                            else
                                                  if (message.length()==0)                      // nothing received after timeout
                                                    {
                                                    String resEth=GetEthernetEhouseState();     //checking if state was changed
                                                    String resEhouse1=GetEhouse1State();
                                                    if (Changed)                                //State was changed then send to opened socket
                                                        {
                                                        if (resEth.length()>0)    outs.write(resEth.getBytes());
                                                        if (resEhouse1.length()>0)    outs.write(resEhouse1.getBytes());    
                                                        outs.flush();
                                                        Changed=false;                          //reset changed flag
                                                        }
                                                    }
                                    }
                                else        //received null    = communication error
                                    {
                                    break;  //finish the loop
                                    }
				}
				catch(Exception classnot)   //get some exception  then break
                                    {
                                    System.err.println("Data received in unknown format: "+classnot.getMessage());
                                    break;
                                    }
	                       
                                
                            if (ehousecommunication.Terminate) break;
                            } 
                            while (!message.equals("bye"));
                    }
		catch(IOException ioException)
                    {
                    System.out.print("Error in server instance #"+String.valueOf(CurrentInstance)+" : "+ioException.getMessage());
                    }
		finally
                    {
			try{
                                System.out.print("closing server instance #"+String.valueOf(CurrentInstance));
				ins.close();
				outs.close();
                            }
			catch(IOException ioException)
                            {
				System.out.print("Error closing server instance #"+String.valueOf(CurrentInstance)+" : "+ioException.getMessage());
                            }
                        try
                            {
                                newsock.close();
                            }
                        catch (Exception e)
                            {
                            	System.out.print("Error closing socketserver instance #"+String.valueOf(CurrentInstance)+" : "+e.getMessage());
                            }
                        ins=null;
                        outs=null;
                        newsock=null;
                        EhouseTCP.CurrentORSensorConnection--;  //decrement counter of active connections
                        
                    }
	}
    
    
}
