/* * Copyright (c) 2011 Matthew Doll <mdoll at homenet.me>. * * This file is part of HomeNet. * * HomeNet is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HomeNet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HomeNet. If not, see <http://www.gnu.org/licenses/>. */ package homenet; import java.util.*; import java.util.concurrent.*; import static homenet.Packet.*; /** * * @author mdoll */ public class Stack { static int InterruptCount = 0; public int _nodeId; private Queue<Packet> _packets = new ConcurrentLinkedQueue(); private Map<String, Port> _ports = new ConcurrentHashMap(); private Map<Integer, Device> _devices = new ConcurrentHashMap(); private ArrayList<Schedule> _deviceSchedule = new ArrayList(); private ArrayList<Interrupt> _deviceInterrupts = new ArrayList(); private int _uniqueId; private int _scheduleCount; private long _deviceTimer; private ProcessThread _processThread; private List<PacketListener> _packetListeners = new CopyOnWriteArrayList(); public List<PortListener> _portListeners = new CopyOnWriteArrayList(); public Stack(int id) { _nodeId = id; _uniqueId = 0; _processThread = new ProcessThread(this, 10); _processThread.start(); } public void addPacketListener(PacketListener listener){ _packetListeners.add(listener); } public void addPortListener(PortListener listener){ _portListeners.add(listener); } public void addPort(String name, Port port) throws Exception { _ports.put(name, port); port.init(name); for(PortListener l : _portListeners){ l.portAdded(name); } } public void removePort(String name) { Port p = _ports.get(name); if(p != null){ _ports.get(name).stop(); _ports.remove(name); } for(PortListener l : _portListeners){ l.portRemoved(name); } } public Map getPorts() { return _ports; } public Port getPort(String port) { return _ports.get(port); } private int _getId() { if (_uniqueId == 255) { _uniqueId = 0; } return _uniqueId++; } public Packet getNewPacket() { // if (_packets.size() > 10) { // System.out.println("WARNING PacketStack is getting Large. " + _packets.size() + " Packets"); // } System.out.println("Creating New Packet. Stack has " + _packets.size() + " Packets"); Packet packet = new Packet(); addPacket(packet); return packet; } public void addPacket(Packet packet) { // System.out.println("Adding New Packet. Stack has " + _packets.size() + " Packets"); _packets.add(packet); } public void init() { // _ports = new HashMap(); // _devices = new HashMap(); } public void init(Map ports, Map devices) { _ports = ports; _devices = devices; //setup ports Iterator iterator = _ports.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry portSet = (Map.Entry) iterator.next(); // ((Port) portSet.getValue()).init((String) portSet.getKey()); } //setup devices iterator = _devices.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry deviceSet = (Map.Entry) iterator.next(); // ((Port) deviceSet.getValue()).init((String) deviceSet.getKey()); } } public void receive() { for(Port p : _ports.values()){ p.receive(); } } public Packet clonePacket(Packet packet) { //Packet packet2 = new; try { Packet packet2 = (Packet) packet.clone(); addPacket(packet2); return packet2; } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } return new Packet(); } public boolean process() { boolean process = false; for (Packet p : _packets) { if (_processPacket(_packets.peek()) == true) { process = true; } else { _packets.poll(); } } // for (int i = _packets.size() - 1; i >= 0; i--) { // if (_processPacket(_packets.get(i)) == true) { // process = true; // } else { // _packets.remove(i); // } // } return process; } private boolean _processPacket(Packet packet) { switch (packet.getStatus()) { case STATUS_CLEAR: // System.out.println("Packet Status: Clear"); return false; // break; case STATUS_RECEIVING: // System.out.println("Packet Status: Receving"); return false; // break; case STATUS_RECEIVED: // System.out.println("Packet Status: Received"); //debugPacket(packet); //process Listeners for(PacketListener l : _packetListeners){ l.packetRecieved(packet); } if (packet.getToNode() == _nodeId) { //@todo process received packet // packetReceivedEvent(packet); packet.setStatus(STATUS_CLEAR); } else if(_ports.size()> 1) { packet.setStatus(STATUS_READY); break; } System.out.println("Only "+_ports.size()+" port found removing packet"); packet.setStatus(STATUS_CLEAR); break; case STATUS_WAITING: // System.out.println("Packet Status: Waiting"); return false; //break; case STATUS_READY: // System.out.println("Packet Status: Ready"); if (packet.getToNode() == _nodeId) { //check for packets to self and process them processCommand(packet); packet.setStatus(STATUS_CLEAR); } //if their are not any ports just let the packets sit if (_ports.isEmpty()) { packet.setStatus(STATUS_FAILED); System.out.println("No ports to process Packets"); break; } //set toPort address. a unique packet is required for each port //system built the packet and it needs to go to all the ports if (packet.fromPort.equals(packet.toPort)) { //the only times these are equal is when it's a brand new pack that needs to go out to all ports System.out.println("system built packet"); Iterator iterator = _ports.keySet().iterator(); packet.toPort = (String) iterator.next(); //set first port System.out.println("packet1 to: " + packet.toPort); while (iterator.hasNext()) { Packet p = clonePacket(packet); p.toPort = (String) iterator.next(); // @todo display debug packet // debugPacket(p); System.out.println("packet2 to: " + p.toPort); System.out.println("packet1 to: " + packet.toPort); } //packet came from a port and needs to passed to the other ports //@todo can this just be an else? } else if (packet.toPort == null) { //has a fromPort but no toPort System.out.println("to port null"); Iterator iterator = _ports.keySet().iterator(); while (iterator.hasNext()) { String port = (String) iterator.next(); if (!packet.fromPort.equals(port)) { if (packet.toPort == null) { packet.toPort = port; } else { System.out.println("Cloning packet"); Packet p = clonePacket(packet); p.toPort = port; } } } } System.out.println("toPort: " + packet.getToPort()); //=null? //this will not try and send packets when there isn't a port to send it to if ((packet.getToPort() != null ) && (((Port) _ports.get(packet.getToPort())).isSending() == false)) { System.out.println("changing status to send"); packet.setStatus(STATUS_SENDING); } else { //allow a pause if it can't send return false; } // only mark packet per type to send per loop. sending serial is blocking. //if reset pointer and set to send break; case STATUS_SENDING: // System.out.println("Packet Status: Sending"); //because of the above, only one packet will be changed to sending, this keeps from blocking the loop too long //debugPacket(packet); if (_ports.containsKey(packet.toPort)) { //check to see if homenet has booted up //print("Trying to send: "+_ports.get(packet.toPort).started); if (_ports.get(packet.toPort).started == true) { System.out.println("Status: Packet Sending: " + packet.toPort); _ports.get(packet.toPort).send(packet); } else if (_ports.get(packet.toPort).timeAdded + NODE_BOOT_TIME < System.currentTimeMillis()) { _ports.get(packet.toPort).started = true; //@todo display message //msg("Serial Port " + packet.toPort + " Started"); } } else { System.out.println("Missing Port, Removing Packet"); packet.setStatus(STATUS_FAILED); } break; case STATUS_SENT: System.out.println("Packet Status: Sent"); if (packet.getType() == 1) { packet.setStatus(STATUS_ACK); //start packets ack timer } else { packet.setStatus(STATUS_SUCCESS); } break; case STATUS_ACK: packet.setStatus(STATUS_CLEAR); //if execeded timer resen //count resends if exceds max kill packet and add error break; case STATUS_SUCCESS: System.out.println("Packet Status: Success"); //option to post status to screen packet.setStatus(STATUS_CLEAR); break; case STATUS_FAILED: System.out.println("Packet Status: Failed"); //option to post status to screen packet.setStatus(STATUS_CLEAR); break; } return true; } private boolean processCommand(Packet packet) { int device = packet.getToDevice(); PartialPacket partial = _devices.get(device).process(packet.getFromNode(), packet.getFromDevice(), packet.getCommand(), packet.getPayload()); if (partial != null) { // if(packetType == 2){ addUdpPacket(partial.fromDevice, packet.getFromNode(), packet.getFromDevice(), partial.command, partial.payload); } return true; } Payload packetToPayload(Packet packet) { Payload payload = new Payload(); java.lang.System.arraycopy(packet.data, 0, payload.data, 0, packet.getLength()); // for (int i = 0; i < packet.getLength(); i++) { // payload.data[i] = packet.data[i]; // } return payload; } public int getNodeId() { return _nodeId; } public Packet addTcpPacket(int fromDevice, int toNode, int toDevice, int command, Payload payload) { return addTcpPacket(fromDevice, toNode, toDevice, command, payload, STATUS_READY); } public Packet addTcpPacket(int fromDevice, int toNode, int toDevice, int command, Payload payload, int status) { Packet packet = getNewPacket(); packet.setStatus(STATUS_READY); packet.setLength(payload.length + OFFSET_PACKET); //header 8 crc 2 packet.setType(1); packet.setFromNode(_nodeId); packet.setFromDevice(fromDevice); packet.setToNode(toNode); packet.setToDevice(toDevice); packet.setId(_getId()); packet.setCommand(command); packet.setPayload(payload); return addCrc(packet); } public Packet addUdpPacket(int fromDevice, int toNode, int toDevice, int command, Payload payload) { return addUdpPacket(fromDevice, toNode, toDevice, command, payload, STATUS_READY); } public Packet addUdpPacket(int fromDevice, int toNode, int toDevice, int command, Payload payload, int status) { Packet packet = getNewPacket(); packet.setStatus(STATUS_READY); packet.setLength(payload.length + OFFSET_PACKET); //header 8 crc 2 packet.setType(2); packet.setFromNode(_nodeId); packet.setFromDevice(fromDevice); packet.setToNode(toNode); packet.setToDevice(toDevice); packet.setId(_getId()); packet.setCommand(command); packet.setPayload(payload); return addCrc(packet); } public void debugPayload(final Payload payload) { System.out.println("==Debug Payload=="); System.out.print("Length: "); System.out.println(payload.length); System.out.print("Payload: "); for (int i = 0; i < payload.length; i++) { System.out.print(payload.data[i]); System.out.print(","); } System.out.println(""); } public Packet addCrc(Packet packet) { int dataChecksum = 0; int i; for (i = 0; i < (packet.getLength() - OFFSET_FOOTER); i++) { dataChecksum = crc16_update(dataChecksum, (int) (packet.data[i] & 0xFF)); } packet.data[i++] = (byte) ((dataChecksum >> 8) & 0xFF); packet.data[i] = (byte) (dataChecksum & 0xFF); return packet; } public void deviceUpdate() { for (int i = 0; i < _devices.size(); i++) { _devices.get(i).updateOften(); } if (System.currentTimeMillis() > (_deviceTimer + DEVICE_UPDATE)) { for (int i = 0; i < _devices.size(); i++) { _devices.get(i).update(); } _scheduleCount++; _deviceTimer = System.currentTimeMillis(); } } public void registerSchedule(ArrayList s) { _deviceSchedule = s; } public void registerInterrupts(ArrayList i) { _deviceInterrupts = i; } public void deviceSchedule() { //_scheduleCount++; if (_scheduleCount == DEVICE_SCHEDULE) { //PayloadBuffer buffer; for (int i = 0; i < _deviceSchedule.size(); i++) { if (_deviceSchedule.get(i).delay == 0) { PartialPacket partial = _deviceSchedule.get(i).device.schedule(_deviceSchedule.get(i).command, _deviceSchedule.get(i).payload); if (partial != null) { addUdpPacket(partial.fromDevice, _deviceSchedule.get(i).toNode, _deviceSchedule.get(i).toDevice, partial.command, partial.payload); } _deviceSchedule.get(i).delay = _deviceSchedule.get(i).frequency - 1; } else { _deviceSchedule.get(i).delay--; } } _scheduleCount = 0; } } public void deviceInterrupt() { deviceInterrupt(false); } public void deviceInterrupt(boolean run) { if ((InterruptCount > 0) || run) { InterruptCount = 0; for (int i = 0; i < _deviceInterrupts.size(); i++) { PartialPacket partial = _deviceInterrupts.get(i).device.interrupt(_deviceInterrupts.get(i).command, _deviceInterrupts.get(i).payload); if (partial != null) { addUdpPacket(partial.fromDevice, _deviceInterrupts.get(i).toNode, _deviceInterrupts.get(i).toDevice, partial.command, partial.payload); } } } } public void loop() { //receive incoming packets receive(); process(); deviceUpdate(); //still working on how often to call these receive(); process(); deviceSchedule(); receive(); process(); deviceInterrupt(); } public int getVersion() { return 0x01; } //ported from the avr crc lib class ProcessThread extends Thread { boolean running; // Is the thread running? Yes or no? int wait; // How many milliseconds should we wait in between executions? String id; // Thread name Stack _homeNet; int count; // Constructor, create the thread // It is not running by default ProcessThread(Stack h, int w) { _homeNet = h; wait = w; running = false; count = 0; } // Overriding "start()" @Override public void start() { // Set running equal to true running = true; // Print messages System.out.println("Starting HomeNet Process Stack Thread (will execute every " + wait + " milliseconds.)"); // Do whatever start does in Thread, don't forget this! super.start(); } // We must implement run, this gets triggered by start() @Override public void run() { while (running) { count = 0; _homeNet.receive(); while (_homeNet.process() == true) { count++; }; // Ok, let's wait for however long we should wait try { if (count > 1) { System.out.println("Processed Stack " + count + " times"); } Thread.sleep((long) (wait)); } catch (Exception e) { } } System.out.println(id + " thread is done!"); // The thread is done when we get to the end of run() } // Our method that quits the thread public void quit() { System.out.println("Quitting."); running = false; // Setting running to false ends the loop in run() // IUn case the thread is waiting. . . interrupt(); } } };