001/*
002 *  $Rev: 1021 $:     Revision of last commit
003 *  $Author: tgutwin $:  Author of last commit
004 *  $Date: 2015-10-28 15:59:29 -0700 (Wed, 28 Oct 2015) $:    Date of last commit
005 *  $URL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/tools/lednet/LedNetProxySocketServer.java $
006 */
007/*
008 *
009 *  Written by Tom Gutwin - WebARTS Design.
010 *  Copyright (C) 2016-2017 WebARTS Design, North Vancouver Canada
011 *  http://www.webarts.bc.ca
012 *
013 *  This program is free software; you can redistribute it and/or modify
014 *  it under the terms of the GNU General Public License as published by
015 *  the Free Software Foundation; either version 2 of the License, or
016 *  (at your option) any later version.
017 *
018 *  This program is distributed in the hope that it will be useful,
019 *  but WITHOUT ANY WARRANTY; without_ even the implied warranty of
020 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
021 *  GNU General Public License for more details.
022 *
023 *  You should have received a copy of the GNU General Public License
024 *  along with this program; if not, write to the Free Software
025 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
026 */
027
028package ca.bc.webarts.tools.lednet;
029
030import java.io.DataInputStream;
031import java.io.PrintStream;
032import java.io.IOException;
033import java.util.Arrays;
034import java.net.Socket;
035import java.net.ServerSocket;
036import java.util.concurrent.ArrayBlockingQueue;
037
038//import ca.bc.webarts.tools.NativeAppLauncher;
039//import ca.bc.webarts.widgets.ProcessRunner;
040import ca.bc.webarts.tools.TCPSocketServer;
041
042/**
043 * Proxy Server for <a href="http://forum.universal-devices.com/topic/19913-magic-ufo-controller-for-rgbw">ledeNet</a> Magic led STRIP LIGHT WiFI enabled CONTROLLER.<br>
044 * <img src="https://fred.webarts.bc.ca:9443/roller/tom/mediaresource/372c0d8a-c3bf-4294-89b7-66293851a567?t=true" align="right"/>
045 * A Multi-Threaded TCP Socket server that listens for requests and then performs specific functions
046 * (wrapped in the {@link clentThread() clentThread}) on the server.
047 * <br />It makes calls to my
048 * <a href="https://fred.webarts.bc.ca:9443/roller/tom/mediaresource/372c0d8a-c3bf-4294-89b7-66293851a567">Controller Software class.</a>
049 * It acts as a Network based listener to relay requests to the lednET led STRIP LIGHT CONTROLLER.<br /><br />
050 * Simply start it up<br />
051 * Usage: <pre>
052          java ca.bc.webarts.tools.lednet.LedNetProxySocketServer &lt;portNumber&gt;
053  </pre>
054 * Then send TCP messages to its port directly<br /> OR<br />http get requests to the port with parameters<br />
055 * For example:
056 * <ul>
057     <li><a href="http://fred.webarts.bc.ca:44445/b">http://fred.webarts.bc.ca:44445/b</a></li>
058     <li><a href="http://localhost:44445/colour?r=5">http://localhost:44445/colour?r=5</a></li>
059   </ul>
060 * params it listens and relays are: <pre>on, off, r, g, b, p, y, w, ww, cw, cyan, fun, nightfade, colour?r=10&g=2&b=14</pre><br />
061 *
062 **/
063public class LedNetProxySocketServer extends TCPSocketServer
064{
065  public static final int DEFAULT_LEDNETPROXY_PORT = DEFAULT_PORT+1;
066  LedNetProxy instance = new LedNetProxy();
067
068
069  /** Default Constructor using defaults. **/
070  public LedNetProxySocketServer()
071  {
072    portNumber_ = DEFAULT_LEDNETPROXY_PORT;
073    initPortProcessors();
074  }
075
076
077  /**  Constructor to allow a custom port. **/
078  public LedNetProxySocketServer(int portNum)
079  {
080    portNumber_ = portNum;
081    initPortProcessors();
082  }
083
084
085  /** call this when a new connection comes in to start the procesing of the action that is taken. **/
086  public boolean postConnection(Socket s, int maxBlockTime_ms  )
087  {
088    int waiter = 0;int sleepTime=250;  //int maxBlockTime_ms = 1500
089    if (acceptingConnections_)
090    {
091      clientSocket_=s;
092      Thread ct = new lednetClientThread(clientSocket_); // does something with the request
093      if (debug_>1) System.out.println("  >Posting a new client connection: ");
094      while(!clientQueue_.offer(ct))
095      {
096        while( waiter<maxBlockTime_ms) {ca.bc.webarts.widgets.Util.sleep(sleepTime);waiter+=sleepTime;}
097        //System.out.println("Maximum Clients Reached: "+MAX_CLIENTS_COUNT);
098      }
099    }
100
101    boolean retVal = acceptingConnections_;
102    if (acceptingConnections_) retVal = (waiter<maxBlockTime_ms);
103    return retVal;
104  }
105
106
107  /**
108  * Starts the port listener server.
109  * Usage: <pre>
110          java ca.bc.webarts.tools.lednet.LedNetProxySocketServer &lt;portNumber&gt;
111  </pre>
112  **/
113  public static void main(String args[])
114  {
115    int portToUse= LedNetProxySocketServer.DEFAULT_LEDNETPROXY_PORT;
116    if (args.length < 1)
117    {
118      System.out.println("Usage: java ca.bc.webarts.tools.lednet.LedNetProxySocketServer <portNumber>\n" + "Now using DEFAULT port number=" + portToUse);
119    }
120    else
121    {
122      try
123      {
124        portToUse= Integer.valueOf(args[0]).intValue();
125      }
126      catch (Exception ex){} // use defaults
127    }
128    System.out.println("LedNetProxySocketServer Started.\n" +"Now using port number=" + portToUse);
129    LedNetProxySocketServer tcpServer = new LedNetProxySocketServer(portToUse);
130
131    int i = 0;
132    boolean posted = false;
133    while (tcpServer.getSocket()!=null)
134    {
135      try
136      {
137        // Listening  for another client connection
138        if (debug_>1) System.out.println("Accepting new connections ");
139        posted = tcpServer.postConnection(tcpServer.getSocket().accept()); // accept blocks until a connection comes in
140
141        //if (debug_>1) System.out.println("connectionPosted> "+posted);
142        if (!posted)
143        {
144          if (debug_>0) System.out.println(" NOT Posted: clientThreads MAXXED: ");
145        }
146      }
147      catch (IOException e)
148      {
149        System.out.println(e);
150      }
151    }
152     if (debug_>0) System.out.println("Done, Exiting");
153  }
154
155/* ******************** */
156  /**
157   * The  client thread (part of the TCPSocketServer)  that gets created when a connectiuon comes in.
158   * This client thread opens the input and the output streams for a particular client,
159   * gets the client request, executes it and terminates.
160   *
161   * @see TCPSocketServer
162   */
163  class lednetClientThread extends Thread
164  {
165    int debug_=2;
166    private String clientRequest = null;
167    private DataInputStream is = null;
168    private PrintStream os = null;
169    private Socket clientSocket = null;
170
171
172    public lednetClientThread(Socket clientSocket)
173    {
174      this.clientSocket = clientSocket;
175    }
176
177
178    public void run()
179    {
180      String TEMP_DIR = System.getProperty("java.io.tmpdir");
181      try
182      {
183        /*
184         * Create input and output streams for this client.
185         */
186        if (debug_>1) System.out.println("   >Starting LedNetProxySocketServer.lednetClientThread: "+ Thread.currentThread().getId());
187
188        is = new DataInputStream(clientSocket.getInputStream());
189        os = new PrintStream(clientSocket.getOutputStream());
190        String clientRequest = "";
191
192        //os.println("?");
193        clientRequest = is.readLine().trim();
194        System.out.println("   > ClientRequest: "+clientRequest);
195        String[] cmdArgs = clientRequest.split("\\s");
196        java.util.List <String> cmdList = java.util.Arrays.asList(cmdArgs);
197
198        /* Execute the request. */
199        synchronized (this)
200        {
201          System.out.println("   --> Doing something with the socket request");
202          proxyCommand( clientRequest);
203          os.println(TCPSocketServer.END);
204        }
205       if (debug_>1) System.out.println("   >Closing clientThread: "+ Thread.currentThread().getId());
206
207        /*
208         * Close the output stream, close the input stream, close the socket.
209         */
210        is.close();
211        os.close();
212        clientSocket.close();
213      }
214      catch (IOException e)
215      {
216      }
217    }
218
219
220    private void proxyCommand(String clientRequest)
221    {
222      clientRequest=clientRequest.trim();
223      String[] cmdArgs = clientRequest.split("\\s");
224      String[] parmArgs = null;
225      java.util.List <String> cmdList = java.util.Arrays.asList(cmdArgs);
226
227      boolean httpRequest = false;   // if using a browser
228      if(clientRequest.startsWith("GET /"))
229      {
230        System.out.println("Got a Browser GET request '"+clientRequest+"'");
231        httpRequest = true;
232        clientRequest = clientRequest.substring("GET /".length(), clientRequest.length()-" HTTP/1.1".length() ).trim();
233        System.out.println("        clientRequest '"+clientRequest+"'");
234        if(clientRequest.contains("?")) // url has parameters
235        {
236          String parms = clientRequest.substring(clientRequest.indexOf("?")+1);
237          System.out.println("     with parameters: "+parms);
238          clientRequest.substring(0,clientRequest.indexOf("?"));
239          parmArgs = parms.split("&");
240          System.out.println("       "+Arrays.toString(parmArgs));
241        }
242      }
243
244      System.out.println("    Proxying the LEDNet '"+clientRequest+"' command.");
245      System.out.println("       "+Arrays.toString(cmdArgs));
246      if (clientRequest.equalsIgnoreCase("on"))
247      {
248        //instance.sendQueryOnOffCommand(true, true);
249        String queryResponse =  instance.sendQueryOnOffCommand(true, true);
250      }
251      else if (clientRequest.equalsIgnoreCase("off"))
252      {
253        //instance.sendQueryOnOffCommand(true, true);
254        String queryResponse =  instance.sendQueryOnOffCommand(false, true);
255      }
256      else if (clientRequest.equalsIgnoreCase("r"))
257      {
258        //instance.sendQueryOnOffCommand(true, true);
259        instance.sendColourCommand(255,0,0,0);
260      }
261      else if (clientRequest.equalsIgnoreCase("g"))
262      {
263        //instance.sendQueryOnOffCommand(true, true);
264        instance.sendColourCommand(0,255,0,0);
265      }
266      else if (clientRequest.equalsIgnoreCase("b"))
267      {
268        //instance.sendQueryOnOffCommand(true, true);
269        instance.sendColourCommand(0,0,255,0);
270      }
271      else if (clientRequest.equalsIgnoreCase("p"))
272      {
273        //instance.sendQueryOnOffCommand(true, true);
274        instance.sendColourCommand(5,0,14,0);
275      }
276      else if (clientRequest.equalsIgnoreCase("y"))
277      {
278        //instance.sendQueryOnOffCommand(true, true);
279        instance.sendColourCommand(255,255,0,0);
280      }
281      else if (clientRequest.equalsIgnoreCase("w"))
282      {
283        //instance.sendQueryOnOffCommand(true, true);
284        instance.sendColourCommand(200,200,200,255);
285      }
286      else if (clientRequest.equalsIgnoreCase("ww"))
287      {
288        //instance.sendQueryOnOffCommand(true, true);
289        instance.sendColourCommand(0,0,0,255);
290      }
291      else if (clientRequest.equalsIgnoreCase("cw"))
292      {
293        //instance.sendQueryOnOffCommand(true, true);
294        instance.sendColourCommand(240,240,255,80);
295      }
296      else if (clientRequest.equalsIgnoreCase("cyan"))
297      {
298        //instance.sendQueryOnOffCommand(true, true);
299        instance.sendColourCommand(0,20,10,0);
300      }
301      else if (clientRequest.equalsIgnoreCase("dumplevels"))
302      {
303        //instance.sendQueryOnOffCommand(true, true);
304        instance.dumpLevels();
305        instance.dumpReverseLevels();
306      }
307      else if (cmdArgs[0].equalsIgnoreCase("fun"))
308      {
309        //instance.sendQueryOnOffCommand(true, true);
310        instance.fun((cmdArgs.length>1?Integer.parseInt(cmdArgs[1]):-1),
311                     (cmdArgs.length>2?Integer.parseInt(cmdArgs[2]):200));  // loopTime, pause
312      }
313      else if (clientRequest.startsWith("colour") || cmdArgs[0].equalsIgnoreCase("colour"))
314      {
315        int r = 0;
316        int g = 0;
317        int b = 0;
318        int w = 0;
319
320        if(httpRequest)
321        {
322          if(parmArgs!=null)
323          for(int i=0; i<parmArgs.length;i++)
324          {
325            if(parmArgs[i].startsWith("r=")) r=Integer.parseInt(parmArgs[i].substring(parmArgs[i].indexOf("=")+1));
326            if(parmArgs[i].startsWith("g=")) g=Integer.parseInt(parmArgs[i].substring(parmArgs[i].indexOf("=")+1));
327            if(parmArgs[i].startsWith("b=")) b=Integer.parseInt(parmArgs[i].substring(parmArgs[i].indexOf("=")+1));
328            if(parmArgs[i].startsWith("w=")) w=Integer.parseInt(parmArgs[i].substring(parmArgs[i].indexOf("=")+1));
329          }
330          System.out.println("      Parsing parmArgs:"+Arrays.toString( parmArgs));
331          System.out.println("         r="+r+"  g="+g+ "  b="+b+"  w="+w);
332        }
333        else
334        {
335          r = (cmdArgs.length>1?Integer.parseInt(cmdArgs[1]):0);
336          g = (cmdArgs.length>2?Integer.parseInt(cmdArgs[2]):0);
337          b = (cmdArgs.length>3?Integer.parseInt(cmdArgs[3]):0);
338          w = (cmdArgs.length>4?Integer.parseInt(cmdArgs[4]):0);
339        }
340
341        //instance.sendQueryOnOffCommand(true, true);
342        instance.sendColourCommand(r,g,b,w);
343      }
344      else if (cmdArgs[0].equalsIgnoreCase("nightFade"))
345      {
346        instance.sendQueryOnOffCommand(true, false);
347
348        while(true)
349        {
350          instance.stepBetween(1,0,1,0,   8,0,16,0);
351          instance.stepBetween(8,0,16,0,  1,0,1,0);
352        }
353      }
354    }
355  }
356}