/** * Copyright 2014 Comcast Cable Communications Management, LLC * * This file is part of CATS. * * CATS 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. * * CATS 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 CATS. If not, see <http://www.gnu.org/licenses/>. */ package com.comcast.cats.service.power; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class allows for power toggling of the older NETBOOTER type powerbars. * <b>Note</b>: The hardware seems to be a little picky about sending commands * to it quickly. Therefore, there is a small delay between power commands. * @see PAUSE_BETWEEN_CMD */ public class NetBooter_NP_1601D_PowerDevice extends NetBooter_PowerDevice { /** * Small delay used between connection and sending a command to the hardware. */ public static final int PAUSE_BETWEEN_CMD = 200; /** * The device prompt. */ public static final String PROMPT = ">"; /** * The power command used for outlet state control. */ public static final String PWRCMD = "pset"; /** * The reboot command used for outlet toggling. */ public static final String REBOOT = "rb"; /** * The command to display outlet status. */ public static final String OUTLETSTATUS = "pshow"; /** * The ON state for the outlet. */ public static final String ONSTATE = "1"; /** * The OFF state for the outlet. */ public static final String OFFSTATE = "0"; private static final int responseTime = 500; private static final int CONNECT_TIMEOUT = 10000; private final Logger log = LoggerFactory.getLogger(NetBooter_NP_1601D_PowerDevice.class); public NetBooter_NP_1601D_PowerDevice() { } /** * The main power command used for changing the outlet power state. * * @param cmd * The command to perform (ON, OFF, or BOOT). * @param outlet * The outlet number. * @return true if the power state was changed successfully, false otherwise. */ public boolean power(String cmd, final int outlet) { assert (null != cmd); cmd = cmd.trim(); if (outlet < 0 || outlet > getNumOutlets()) { throw new java.lang.IllegalArgumentException("Invalid outlet number: " + outlet); } String np_command; if (cmd.equalsIgnoreCase(OFF)) { np_command = PWRCMD + " " + outlet + " " + OFFSTATE; } else if (cmd.equalsIgnoreCase(ON)) { np_command = PWRCMD + " " + outlet + " " + ONSTATE; } else if (cmd.equalsIgnoreCase(BOOT)) { np_command = REBOOT + " " + outlet; } else { throw new java.lang.IllegalArgumentException( "Invalid command argument: " + cmd); } log.info("Sending command: [" + np_command + "] to " + this.getIp()); if (client.connect(CONNECT_TIMEOUT)) { String response = client.read(responseTime); if (client.sendCmd(np_command, true)) { response = client.read(responseTime); if (response.indexOf("Invalid command") == -1) { updateStatistics(outlet, cmd, true); client.close(); return true; } } } updateStatistics(outlet, cmd, false); client.close(); return false; } /** * {@inheritDoc} */ @Override public synchronized String getOutletStatus(int outlet) { String status = "UNKNOWN"; if (client.connect(CONNECT_TIMEOUT)) { String response = client.waitForString(PROMPT, responseTime); sleep(PAUSE_BETWEEN_CMD); if (!client.sendCmd(OUTLETSTATUS, false)) { client.close(); return status; } response = client.read(responseTime); if (null != response && !response.isEmpty()) { // NP16: // "-----+------------+---------+-------------|---------|----------"; // New type: // "----+----------------+------+----------------+--------"; String maker = "----.*--------\n"; String[] tableArray = response.split(maker); if (tableArray.length != 2) { client.close(); log.error("Unknown table format, cannot parse, response: " + response); return status; } else { /* The second element of the split table should be * a table of each outlet status. * Format is as follows: * | Plug | Name | Status | Reserved By | Timer | AutoPing | * * We are interested in the plug (outlet #) and the status only. */ response = tableArray[1]; } final int OUTLET_COL = 0; final int STATUS_COL = 2; String[] rows = response.trim().split("\\n\\r"); for (int row = 0; row <= rows.length; ++row) { String[] cols = rows[row].split("\\|"); String outletStr = ""; String statusStr = ""; try { outletStr = cols[OUTLET_COL].trim(); statusStr = cols[STATUS_COL].trim(); if (outlet == Integer.parseInt(outletStr)) { if (statusStr.equalsIgnoreCase(PowerControllerDevice.OFF)) { status = PowerControllerDevice.OFF; } else if (statusStr.equalsIgnoreCase(PowerControllerDevice.ON)) { status = PowerControllerDevice.ON; } else { log.info("Invalid status found: [" + statusStr + "]"); } break; } } catch (NumberFormatException npe) { log.error("NumberFormatException parsing [" + outletStr + "] row format may have changed."); } catch (IndexOutOfBoundsException iob) { log.error("IndexOutOfBoundsException table format may have changed."); } } } } else { if (!client.isConnected()) { log.warn("Could not connect to client."); } else { log.warn("Status request failed."); } } client.close(); return status; } }