/*
* The MIT License (MIT)
*
* Copyright (c) 2012 Curt Binder
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package info.curtbinder.reefangel.service;
import info.curtbinder.reefangel.controller.Controller;
import info.curtbinder.reefangel.controller.DateTime;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
public class XMLHandler extends DefaultHandler {
private static final String TAG = XMLHandler.class.getSimpleName();
private String currentElementText = "";
private String requestType = "";
private Controller ra;
private String version = "";
private String memoryResponse = "";
private String modeResponse = "";
// expansion relays
// for 0.8.5.x use 0 based data
// for 0.9.x and later use 1 based data
private boolean fUse085XRelays = false;
private DateTime dt;
public Controller getRa ( ) {
return ra;
}
public String getVersion ( ) {
return version;
}
public String getDateTime ( ) {
return dt.getDateTimeString();
}
public String getDateTimeUpdateStatus ( ) {
return dt.getUpdateStatus();
}
public String getMemoryResponse ( ) {
return memoryResponse;
}
public String getModeResponse ( ) {
return modeResponse;
}
public String getRequestType ( ) {
return requestType;
}
public XMLHandler () {
super();
this.ra = new Controller();
this.dt = new DateTime();
}
public void setOld085xExpansion ( boolean f ) {
this.fUse085XRelays = f;
}
public void endDocument ( ) throws SAXException {
if ( ra.getLogDate().equals( "" ) ) {
// No log date found, set the date to be the current date/time
DateFormat dft =
DateFormat.getDateTimeInstance( DateFormat.DEFAULT,
DateFormat.DEFAULT,
Locale.getDefault() );
ra.setLogDate( dft.format( new Date() ) );
}
}
public void characters ( char[] ch, int start, int length )
throws SAXException {
String s = new String( ch, start, length );
currentElementText += s;
}
public void endElement ( String uri, String localName, String qName )
throws SAXException {
String tag;
if ( !qName.equals( "" ) ) {
// Log.d(TAG, "end xml: qName");
tag = qName;
} else {
// Log.d(TAG, "end xml: localName");
tag = localName;
}
// if ( (requestType.equals( RequestCommands.Status )) ||
// (requestType.startsWith( RequestCommands.Relay )) ) {
if ( requestType.equals( RequestCommands.Status ) ) {
if ( tag.equals( XMLTags.Status ) ) {
return;
} else {
// Parameter status and Labels are sent using the same XML outer
// tag
if ( tag.endsWith( XMLTags.LabelEnd )
&& !tag.equals( XMLTags.RelayMaskOn ) ) {
processLabelXml( tag );
} else {
processStatusXml( tag );
}
}
} else if ( requestType.equals( RequestCommands.MemoryByte ) ) {
if ( tag.equals( XMLTags.Memory ) ) {
return;
} else {
processMemoryXml( tag );
}
} else if ( requestType.equals( RequestCommands.DateTime ) ) {
if ( tag.equals( XMLTags.DateTime ) ) {
if ( !currentElementText.equals( "" ) ) {
// not empty meaning we have a status to report
// either OK or ERR
dt.setStatus( currentElementText );
}
return;
} else {
processDateTimeXml( tag );
}
} else if ( requestType.equals( RequestCommands.Version ) ) {
processVersionXml( tag );
} else if ( requestType.equals( RequestCommands.PwmOverride ) ) {
processPWMOverrideResponseXml(tag);
} else if ( requestType.equals( RequestCommands.ExitMode ) ) {
processModeXml( tag );
} else {
// TODO request none, set an error?
Log.d( TAG, "Unknown Request: " + requestType );
}
currentElementText = "";
}
//
public void startElement (
String uri,
String localName,
String qName,
Attributes attributes ) throws SAXException {
String tag;
if ( !qName.equals( "" ) ) {
// Log.d(TAG, "start xml: qName");
tag = qName;
} else {
// Log.d(TAG, "start xml: localName");
tag = localName;
}
// Log.d(TAG, "start: r'" + requestType + "', t'" + tag + "'");
if ( requestType.equals( "" ) ) {
// no request type, so set it based on the first element we process
if ( tag.equals( XMLTags.Status ) ) {
requestType = RequestCommands.Status;
} else if ( qName.equals( XMLTags.DateTime ) ) {
requestType = RequestCommands.DateTime;
} else if ( tag.equals( XMLTags.Version ) ) {
requestType = RequestCommands.Version;
} else if ( tag.equals( XMLTags.Mode ) ) {
// all modes return the same response, just chose to use Exit
// Mode
requestType = RequestCommands.ExitMode;
} else if ( tag.startsWith( XMLTags.MemorySingle ) ) {
// can be either type, just chose to use Bytes
requestType = RequestCommands.MemoryByte;
} else if ( tag.startsWith( XMLTags.PWMOverrideResponse ) ) {
requestType = RequestCommands.PwmOverride;
} else {
Log.d(TAG, "startElement: (Unknown): " + tag );
requestType = RequestCommands.None;
}
}
}
private void processStatusXml ( String tag ) {
if ( tag.equals( XMLTags.T1 ) ) {
ra.setTemp1( Integer.parseInt( currentElementText ) );
} else if ( tag.equals( XMLTags.T2 ) ) {
ra.setTemp2( Integer.parseInt( currentElementText ) );
} else if ( tag.equals( XMLTags.T3 ) ) {
ra.setTemp3( Integer.parseInt( currentElementText ) );
} else if ( tag.equals( XMLTags.PH ) ) {
ra.setPH( Integer.parseInt( currentElementText ) );
} else if ( tag.equals( XMLTags.PHExpansion ) ) {
ra.setPHExp( Integer.parseInt( currentElementText ) );
} else if ( tag.equals( XMLTags.ATOLow ) ) {
boolean f = false;
if ( Short.parseShort( currentElementText ) == 1 ) {
f = true;
}
ra.setAtoLow( f );
} else if ( tag.equals( XMLTags.ATOHigh ) ) {
boolean f = false;
if ( Short.parseShort( currentElementText ) == 1 ) {
f = true;
}
ra.setAtoHigh( f );
} else if ( tag.equals( XMLTags.PWMActinic ) ) {
short v = Short.parseShort( currentElementText );
ra.setPwmA( v );
} else if ( tag.equals( XMLTags.PWMDaylight ) ) {
short v = Short.parseShort( currentElementText );
ra.setPwmD( v );
} else if ( tag.startsWith( XMLTags.PWMExpansion ) &&
!tag.endsWith( XMLTags.Override )) {
short channel =
Short.parseShort( tag.substring( XMLTags.PWMExpansion
.length() ) );
short v = Short.parseShort( currentElementText );
ra.setPwmExpansion( channel, v );
} else if ( tag.startsWith( XMLTags.PWMExpansion16) &&
!tag.endsWith( XMLTags.Override ) ) {
short channel = Short.parseShort( tag.substring(XMLTags.PWMExpansion16.length()) );
short v = Short.parseShort( currentElementText );
ra.setSCPwmExpansion( channel, v );
} else if ( tag.equals( XMLTags.Salinity ) ) {
ra.setSalinity( Integer.parseInt( currentElementText ) );
} else if ( tag.equals( XMLTags.ORP ) ) {
ra.setORP( Integer.parseInt( currentElementText ) );
} else if ( tag.equals( XMLTags.WaterLevel ) ) {
// 1 channel water level, tag is WL
short v = Short.parseShort( currentElementText );
ra.setWaterLevel( (short) 0, v );
} else if ( tag.equals( XMLTags.Relay ) ) {
ra.setMainRelayData( Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RelayMaskOn ) ) {
ra.setMainRelayOnMask( Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RelayMaskOff ) ) {
ra.setMainRelayOffMask( Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.LogDate ) ) {
ra.setLogDate( currentElementText );
} else if ( tag.equals( XMLTags.RelayExpansionModules ) ) {
short v = Short.parseShort( currentElementText );
ra.setRelayExpansionModules( v );
} else if ( tag.equals( XMLTags.ExpansionModules ) ) {
short v = Short.parseShort( currentElementText );
ra.setExpansionModules( v );
} else if ( tag.equals( XMLTags.ExpansionModules1 ) ) {
short v = Short.parseShort( currentElementText );
ra.setExpansionModules1( v );
} else if ( tag.equals( XMLTags.Humidity ) ) {
short v = Short.parseShort( currentElementText );
ra.setHumidity( v );
} else if ( tag.equals( XMLTags.AIBlue ) ) {
ra.setAIChannel( Controller.AI_BLUE,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.AIRoyalBlue ) ) {
ra.setAIChannel( Controller.AI_ROYALBLUE,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.AIWhite ) ) {
ra.setAIChannel( Controller.AI_WHITE,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RFMode ) ) {
ra.setVortechValue( Controller.VORTECH_MODE,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RFSpeed ) ) {
ra.setVortechValue( Controller.VORTECH_SPEED,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RFDuration ) ) {
ra.setVortechValue( Controller.VORTECH_DURATION,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RFWhite ) ) {
ra.setRadionChannel( Controller.RADION_WHITE,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RFBlue ) ) {
ra.setRadionChannel( Controller.RADION_BLUE,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RFGreen ) ) {
ra.setRadionChannel( Controller.RADION_GREEN,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RFRed ) ) {
ra.setRadionChannel( Controller.RADION_RED,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RFRoyalBlue ) ) {
ra.setRadionChannel( Controller.RADION_ROYALBLUE,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.RFIntensity ) ) {
ra.setRadionChannel( Controller.RADION_INTENSITY,
Short.parseShort( currentElementText ) );
} else if ( tag.equals( XMLTags.IO ) ) {
ra.setIOChannels(Short.parseShort(currentElementText));
} else if ( tag.equals( XMLTags.DCPumpMode ) ) {
ra.setDCPumpValue( Controller.DCPUMP_MODE, Short.parseShort(currentElementText));
} else if ( tag.equals( XMLTags.DCPumpSpeed ) ) {
ra.setDCPumpValue( Controller.DCPUMP_SPEED, Short.parseShort(currentElementText));
} else if ( tag.equals( XMLTags.DCPumpDuration ) ) {
ra.setDCPumpValue( Controller.DCPUMP_DURATION, Short.parseShort(currentElementText));
} else if ( tag.equals( XMLTags.DCPumpThreshold ) ) {
ra.setDCPumpValue( Controller.DCPUMP_THRESHOLD, Short.parseShort(currentElementText));
} else if ( tag.endsWith( XMLTags.Override ) ) {
processPwmOverride(tag, currentElementText);
} else if ( tag.startsWith( XMLTags.Custom ) ) {
short v = Short.parseShort( tag.substring( XMLTags.Custom.length() ) );
short c = Short.parseShort( currentElementText );
ra.setCustomVariable( v, c );
} else if ( tag.startsWith( XMLTags.WaterLevel ) ) {
// 4 channel water level, tags start with WL
short p = Short.parseShort( tag.substring( XMLTags.WaterLevel.length() ) );
short v = Short.parseShort( currentElementText );
ra.setWaterLevel( p, v );
} else if ( tag.equals( XMLTags.StatusFlags ) ) {
ra.setStatusFlags( Short.parseShort(currentElementText) );
} else if ( tag.equals( XMLTags.AlertFlags ) ) {
ra.setAlertFlags( Short.parseShort(currentElementText) );
} else if ( tag.startsWith( XMLTags.RelayMaskOn ) ) {
int relay = Integer.parseInt( tag.substring( XMLTags.RelayMaskOn.length() ) );
if ( fUse085XRelays )
relay += 1;
ra.setExpRelayOnMask( relay, Short.parseShort( currentElementText ) );
} else if ( tag.startsWith( XMLTags.RelayMaskOff ) ) {
int relay = Integer.parseInt( tag.substring( XMLTags.RelayMaskOff.length() ) );
if ( fUse085XRelays )
relay += 1;
ra.setExpRelayOffMask( relay, Short.parseShort( currentElementText ) );
} else if ( tag.startsWith( XMLTags.Relay ) ) {
try {
int relay = Integer.parseInt( tag.substring( XMLTags.Relay.length() ) );
if ( fUse085XRelays )
relay += 1;
ra.setExpRelayData( relay, Short.parseShort( currentElementText ) );
} catch ( NumberFormatException e ) {
Log.e( TAG, "Invalid XML tag: " + tag );
}
} else if ( tag.equals( XMLTags.MyReefAngelID ) ) {
Log.d( TAG, "Reefangel ID: " + currentElementText );
} else {
Log.d( TAG, "Unhandled XML tag (" + tag + ") with data: "
+ currentElementText );
}
}
private String getTagNumber ( String tag, String start, String end ) {
int ep = tag.indexOf( end );
int bp = start.length();
return tag.substring( bp, ep );
}
private void processPwmOverride ( String tag, String element ) {
// Log.d( TAG, "Override tag (" + tag + ") with data: " + element );
Short value = Short.parseShort( element );
if ( tag.startsWith( XMLTags.PWMActinic ) ) {
ra.setPwmAOverride( value );
} else if ( tag.startsWith( XMLTags.PWMDaylight ) ) {
ra.setPwmDOverride( value );
} else if ( tag.startsWith( XMLTags.PWMExpansion ) ) {
// Get the channel from the tag. The last char is an O.
Short channel = Short.parseShort( tag.substring(
XMLTags.PWMExpansion.length(), tag.length()-1) );
ra.setPwmExpansionOverride( channel, value );
} else if ( tag.startsWith( XMLTags.PWMExpansion16 ) ) {
// Get the channel from the tag. The last char is an O.
Short channel = Short.parseShort( tag.substring(
XMLTags.PWMExpansion16.length(), tag.length()-1) );
ra.setSCPwmExpansionOverride( channel, value );
} else if ( tag.startsWith( XMLTags.AIWhite ) ) {
ra.setAIChannelOverride( Controller.AI_WHITE, value );
} else if ( tag.startsWith( XMLTags.AIBlue ) ) {
ra.setAIChannelOverride( Controller.AI_BLUE, value );
} else if ( tag.startsWith( XMLTags.AIRoyalBlue ) ) {
ra.setAIChannelOverride( Controller.AI_ROYALBLUE, value );
} else if ( tag.startsWith( XMLTags.RFWhite ) ) {
ra.setRadionChannelOverride( Controller.RADION_WHITE, value );
} else if ( tag.startsWith( XMLTags.RFRoyalBlue ) ) {
ra.setRadionChannelOverride( Controller.RADION_ROYALBLUE, value );
} else if ( tag.startsWith( XMLTags.RFRed ) ) {
ra.setRadionChannelOverride( Controller.RADION_RED, value );
} else if ( tag.startsWith( XMLTags.RFGreen ) ) {
ra.setRadionChannelOverride( Controller.RADION_GREEN, value );
} else if ( tag.startsWith( XMLTags.RFBlue ) ) {
ra.setRadionChannelOverride( Controller.RADION_BLUE, value );
} else if ( tag.startsWith( XMLTags.RFIntensity ) ) {
ra.setRadionChannelOverride( Controller.RADION_INTENSITY, value );
}
}
private void processLabelXml ( String tag ) {
// Handle all labels here
if ( currentElementText.equals( "null" ) ) {
Log.d( TAG, tag + " is null, skipping" );
return;
}
if ( tag.startsWith( XMLTags.LabelTempBegin ) ) {
// handle temp sensor labels
int sensor =
Integer.parseInt( getTagNumber( tag,
XMLTags.LabelTempBegin,
XMLTags.LabelEnd ) );
if ( sensor < 0 || sensor > Controller.MAX_TEMP_SENSORS )
Log.e( TAG, "Incorrect sensor number: " + tag );
ra.setTempLabel( sensor, currentElementText );
} else if ( tag.startsWith( XMLTags.PWMExpansion ) ) {
// PWME
short channel = Short.parseShort( getTagNumber( tag, XMLTags.PWMExpansion,
XMLTags.LabelEnd ) );
ra.setPwmExpansionLabel( channel, currentElementText );
} else if ( tag.startsWith( XMLTags.PWMExpansion16 ) ) {
// SCPWME
short channel = Short.parseShort( getTagNumber( tag, XMLTags.PWMExpansion16,
XMLTags.LabelEnd ) );
ra.setSCPwmExpansionLabel( channel, currentElementText );
} else if ( tag.startsWith( XMLTags.PWMActinic + "1" ) ) {
// PWMA
ra.setPwmALabel( currentElementText );
} else if ( tag.startsWith( XMLTags.PWMDaylight + "1" ) ) {
// PWMD
ra.setPwmDLabel( currentElementText );
} else if ( tag.equals( XMLTags.PHExpansion + XMLTags.LabelEnd ) ) {
// PHE
ra.setPHExpLabel( currentElementText );
} else if ( tag.equals( XMLTags.PH + XMLTags.LabelEnd ) ) {
// PH
ra.setPHLabel( currentElementText );
} else if ( tag.startsWith( XMLTags.Salinity ) ) {
// SAL
ra.setSalinityLabel( currentElementText );
} else if ( tag.startsWith( XMLTags.ORP ) ) {
// ORP
ra.setORPLabel( currentElementText );
} else if ( tag.startsWith( XMLTags.Humidity ) ) {
// HUM
ra.setHumidityLabel( currentElementText );
} else if ( tag.equals( XMLTags.WaterLevel + XMLTags.LabelEnd ) ) {
// 1 channel Water Level
ra.setWaterLevelLabel( (short) 0, currentElementText );
} else if ( tag.startsWith( XMLTags.WaterLevel ) ) {
// 4 channel Water Level
short p = Short.parseShort( getTagNumber(tag, XMLTags.WaterLevel, XMLTags.LabelEnd) );
ra.setWaterLevelLabel( p, currentElementText );;
} else if ( tag.startsWith( XMLTags.Custom ) ) {
// C
short v =
Short.parseShort( getTagNumber( tag, XMLTags.Custom,
XMLTags.LabelEnd ) );
ra.setCustomVariableLabel( v, currentElementText );
} else if ( tag.startsWith( XMLTags.IO ) ) {
// IO
short v =
Short.parseShort( getTagNumber( tag, XMLTags.IO,
XMLTags.LabelEnd ) );
ra.setIOChannelLabel( v, currentElementText );
} else if ( tag.startsWith( XMLTags.RFBlue )
|| tag.startsWith( XMLTags.RFGreen )
|| tag.startsWith( XMLTags.RFIntensity )
|| tag.startsWith( XMLTags.RFRed )
|| tag.startsWith( XMLTags.RFRoyalBlue )
|| tag.startsWith( XMLTags.RFWhite ) ) {
// RF labels, not stored only handled to prevent
// errors due to processing like a Relay label because
// they start with R
Log.d( TAG, "RF Label" );
} else if ( tag.startsWith( XMLTags.Relay ) ) {
// handle relay labels
int relay =
Integer.parseInt( getTagNumber( tag, XMLTags.Relay,
XMLTags.LabelEnd ) );
if ( relay < 10 ) {
// main relay
ra.getMainRelay().setPortLabel( relay, currentElementText );
} else {
// expansion relays, so split the port from the relay box
int box = relay / 10;
int port = relay % 10;
ra.getExpRelay( box ).setPortLabel( port, currentElementText );
}
} else {
Log.d( TAG, "Unknown label: (" + tag + ") = " + currentElementText );
}
}
private void processDateTimeXml ( String tag ) {
// Response will be more XML data or OK
if ( tag.equals( XMLTags.Hour ) ) {
dt.setHour( Integer.parseInt( currentElementText ) );
} else if ( tag.equals( XMLTags.Minute ) ) {
dt.setMinute( Integer.parseInt( currentElementText ) );
} else if ( tag.equals( XMLTags.Month ) ) {
// controller uses 1 based for month
// java uses 0 based for month
dt.setMonth( Integer.parseInt( currentElementText ) - 1 );
} else if ( tag.equals( XMLTags.Day ) ) {
dt.setDay( Integer.parseInt( currentElementText ) );
} else if ( tag.equals( XMLTags.Year ) ) {
dt.setYear( Integer.parseInt( currentElementText ) );
}
}
private void processVersionXml ( String tag ) {
// Response will be the Version
if ( tag.equals( XMLTags.Version ) ) {
version = currentElementText;
}
}
private void processMemoryXml ( String tag ) {
// Responses will be either: OK, value, ERR
if ( tag.startsWith( XMLTags.MemorySingle ) ) {
memoryResponse = currentElementText;
}
}
private void processPWMOverrideResponseXml ( String tag ) {
// Responses will be either: OK or ERR
if ( tag.startsWith( XMLTags.PWMOverrideResponse ) ) {
modeResponse = currentElementText;
}
}
private void processModeXml ( String tag ) {
// Response will be either: OK or ERR
if ( tag.startsWith( XMLTags.Mode ) ) {
modeResponse = currentElementText;
}
}
}