All Downloads are FREE. Search and download functionalities are using the official Maven repository.

decodes.polling.PollingThread Maven / Gradle / Ivy

Go to download

A collection of software for aggregatting and processing environmental data such as from NOAA GOES satellites.

The newest version!
/*
 * $Id$
 * 
 * This software was written by Cove Software, LLC ("COVE") under contract
 * to Alberta Environment and Sustainable Resource Development (Alberta ESRD).
 * No warranty is provided or implied other than specific contractual terms 
 * between COVE and Alberta ESRD.
 *
 * Copyright 2014 Alberta Environment and Sustainable Resource Development.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package decodes.polling;

import java.io.IOException;
import java.io.PrintStream;
import java.util.Date;
import java.util.Properties;

import lrgs.common.DcpMsg;
import ilex.util.EnvExpander;
import ilex.util.Logger;
import decodes.datasource.RawMessage;
import decodes.db.Database;
import decodes.db.DbEnum;
import decodes.db.EnumValue;
import decodes.db.Platform;
import decodes.db.PlatformStatus;
import decodes.db.TransportMedium;
import decodes.routing.DacqEventLogger;

/**
 * This class manages a single polling session.
 */
public class PollingThread 
	implements Runnable
{
	private String module = "PollingThread";
	
	/** The controller that owns this thread */
	private PollingThreadController parent;
	
	/** The data source is used for writing messages & setting status */
	private PollingDataSource dataSource;
	
	/** The IO Port that the controller allocated for this polling session */
	private IOPort ioPort = null;
	
	/** This thread will construct a protocol module according to transport medium's
	 * loggerType value.
	 */
	private LoggerProtocol protocol = null;
	
	/** Specifies the platform that this session will poll */
	private TransportMedium transportMedium = null;
	
	private PollingThreadState state = PollingThreadState.Waiting;
	
	/** Number of attempts so far */
	private int numTries = 0;
	
	/** Can be set by controller if multiple tries are allowed. */
	private int maxTries = 1;
	
	private boolean _shutdown = false;
	
	private PlatformStatus platformStatus = null;
	private String saveSessionFile = null;
	private PollSessionLogger pollSessionLogger = null;
	public static PrintStream staticSessionLogger = null;
	private PollException terminatingException = null;
	public static int backlogOverrideHours = 0;
	private DacqEventLogger dacqEventLogger = null;
	private int pollPriority = 3;
	private Date threadStart = null;

	public PollingThread(PollingThreadController parent, PollingDataSource dataSource, 
		TransportMedium transportMedium)
	{
		super();
		this.parent = parent;
		this.dataSource = dataSource;
		this.transportMedium = transportMedium;
		if (transportMedium == null)
			module = "Listen";
		else if (transportMedium.platform != null)
		{
			module = "Poll(" + transportMedium.platform.getSiteName(false) + ")";
			String pps = transportMedium.platform.getProperty("pollPriority");
			if (pps != null)
			{
				try 
				{
					pollPriority = Integer.parseInt(pps.trim());
					info("pollPriority set to " + pollPriority);
				}
				catch(NumberFormatException ex)
				{
					warning("Invalid pollPriority property '" + pps + "' -- ignored.");
				}
			}
		}
		dacqEventLogger = parent.getDacqEventLogger();
	}

	@Override
	public void run()
	{
		debug1("PollingThread starting.");
		threadStart = new Date();
		numTries++;
		terminatingException = null;
		makeSessionLogger();
		String s = module + " " + 
			transportMedium.getMediumType() + ":" + transportMedium.getMediumId()
			+ " type=" + transportMedium.getLoggerType()
			+ " attempt #" + numTries;
		info(s);
		annotate(s);
		PollingThreadState exitState = state;
		try
		{
			platformStatus = dataSource.getPlatformStatus(transportMedium); 

			// Determine the since time from platform status, or use default.
			Date since = new Date(System.currentTimeMillis() - 3600000L * parent.getMaxBacklogHours());
			
			if (backlogOverrideHours != 0)
			{
				// backlog Override is used by poll GUI and command line utility.
				since = new Date(System.currentTimeMillis() - 3600000L * backlogOverrideHours);
			}
			else if (platformStatus.getLastMessageTime() != null
				  && platformStatus.getLastMessageTime().after(since))
			{
				// Backup 15 min fudge factor
				since = new Date(platformStatus.getLastMessageTime().getTime() - 15*60000L);
			}
			// Get at least the minimum number of hours.
			if (System.currentTimeMillis() - since.getTime() < parent.getMinBacklogHours() * 3600000L)
				since = new Date(System.currentTimeMillis() - parent.getMinBacklogHours() * 3600000L);
			
			debug1("Since time set to " + since);
			annotate("Since time set to " + since);

			if (!_shutdown)
				ioPort.connect(transportMedium, this);
			platformStatus.setLastContactTime(new Date());
			
			// Construct protocol according to transportMedium.loggerType
			if (!_shutdown)
				makeProtocol();
			if (protocol == null)
				throw new ConfigException("Protocol is null after makeProtocol -- should never happen.");
			protocol.setPollSessionLogger(pollSessionLogger);
			
			if (!_shutdown)
				protocol.login(ioPort, transportMedium);
			
			if (!_shutdown)
			{
				DcpMsg dcpMsg = protocol.getData(ioPort, transportMedium, since);
				if (dcpMsg != null)
				{
					RawMessage ret = new RawMessage(dcpMsg.getData());
					ret.setOrigDcpMsg(dcpMsg);
					ret.setPlatform(transportMedium.platform);
					ret.setTimeStamp(dcpMsg.getXmitTime());
					dataSource.enqueueMsg(ret);
					platformStatus.setLastMessageTime(new Date());
					platformStatus.setLastFailureCodes("" + dcpMsg.getFailureCode());
					platformStatus.setAnnotation("");
				}
			}
			
			if (protocol.getAbnormalShutdown() != null)
			{
				warning("Abnormal protocol shutdown: " + protocol.getAbnormalShutdown());
				exitState = PollingThreadState.Failed;
			}
			else
			{
				protocol.goodbye(ioPort, transportMedium);
				exitState = PollingThreadState.Success;
			}
		}
		catch (DialException ex)
		{
			String msg = "Cannot connect IOPort: " + ex;
			if (numTries < parent.getPollNumTries())
				info(msg);
			else
				failure(msg);
			exitState = PollingThreadState.Failed;
			platformStatus.setLastErrorTime(new Date());
			platformStatus.setAnnotation(msg);
			terminatingException = ex;
		}
		catch (ConfigException ex)
		{
			String msg = "Configuration error: " + ex;
			failure(msg);
			exitState = PollingThreadState.Failed;
			platformStatus.setLastErrorTime(new Date());
			platformStatus.setAnnotation(msg);
			terminatingException = ex;
		}
		catch (LoginException ex)
		{
			String msg = "Error logging into the station: " + ex;
			failure(msg);
			exitState = PollingThreadState.Failed;
			platformStatus.setLastErrorTime(new Date());
			platformStatus.setAnnotation(msg);
			terminatingException = ex;
		}
		catch (ProtocolException ex)
		{
			String msg = "Error communicating with station: " + ex;
			failure(msg);
			exitState = PollingThreadState.Failed;
			platformStatus.setLastErrorTime(new Date());
			platformStatus.setAnnotation(msg);
			terminatingException = ex;
		}
		finally
		{
			if (ioPort != null)
				ioPort.disconnect();
			if (pollSessionLogger != null)
				pollSessionLogger.close();
			pollSessionLogger = null;
		}
		
		// If failed and num tries expired and there was a protocol.
		if (exitState == PollingThreadState.Failed
		 && protocol != null
		 && numTries == maxTries)
		{
			DcpMsg dcpMsg = protocol.getPartialData();
			if (dcpMsg != null)
			{
				warning("Protocol failed after " + maxTries + " attempts, but there is a partial "
					+ "message of length " + dcpMsg.getData().length + ".");
				RawMessage ret = new RawMessage(dcpMsg.getData());
				ret.setOrigDcpMsg(dcpMsg);
				ret.setPlatform(transportMedium.platform);
				ret.setTimeStamp(dcpMsg.getXmitTime());
				dataSource.enqueueMsg(ret);
				platformStatus.setLastMessageTime(new Date());
				platformStatus.setLastFailureCodes("" + dcpMsg.getFailureCode());
				platformStatus.setAnnotation("PartialMsg");
			}
		}
		
		debug2("Writing platform status for " + transportMedium);		
		dataSource.writePlatformStatus(platformStatus, transportMedium);
		
		// Parent will be calling getState(). Make sure the RS doesn't prematurely
		// exit because no states are waiting.
		state = exitState;
		parent.pollComplete(this);
	}
	
	private void makeSessionLogger()
	{
		if (saveSessionFile != null)
		{
			Properties props = new Properties(System.getProperties());
			props.setProperty("MEDIUMID", transportMedium.getMediumId());
			Platform p = transportMedium.platform;
			if (p != null)
				props.setProperty("SITENAME", p.getSiteName(false));
			String fn = EnvExpander.expand(saveSessionFile, props);
			try
			{
				PrintStream ps = staticSessionLogger != null ? staticSessionLogger : new PrintStream(fn);
				pollSessionLogger = new PollSessionLogger(ps, p.getSiteName(false));
			}
			catch (IOException ex)
			{
				warning("Cannot open session log file: " + fn + ": " + ex);
				pollSessionLogger = null;
			}
		}
		else
			debug1("Not saving session file.");
	}
	
	private void makeProtocol()
		throws ConfigException
	{
		String loggerType = transportMedium.getLoggerType();
		if (loggerType == null || loggerType.trim().length() == 0)
			throw new ConfigException("TransportMedium '" + transportMedium.getTmKey()
				+ "' has an undefined loggerType. This is required to determine how to "
				+ "communicate with the device.");
		DbEnum dbe = Database.getDb().getDbEnum("LoggerType");
		if (dbe == null)
			throw new ConfigException("Database does not have a LoggerType enumeration. "
				+ "Run dbimport on $DCSTOOL_HOME/edit-db/enum/LoggerType.xml");
		EnumValue ev = dbe.findEnumValue(loggerType);
		if (ev == null)
			throw new ConfigException("LoggerType enumeration does not have a value matching '"
				+ loggerType + "', which is used in tranposrt medium '" + transportMedium.getTmKey()
				+ "'. Correct the TM or add the loggerType value with the rledit command.");
		try
		{
			Class protClass = ev.getExecClass();
			protocol = (LoggerProtocol)protClass.newInstance();
			protocol.setDataSource(dataSource);
			protocol.setPollingThread(this);
		}
		catch(ClassNotFoundException ex)
		{
			throw new ConfigException("LoggerType enumeration specifies exec class '"
				+ ev.getExecClassName() + "', which cannot be loaded. Check that the appropriate"
				+ " jar file is accessible, or correct the name with the rledit command: " + ex);
		}
		catch(Exception ex)
		{
			throw new ConfigException("LoggerType enumeration specifies exec class '"
				+ ev.getExecClassName() + "', which cannot be instantiated: " + ex);
		}
	}
	
	/** Prepare to rerun after a failure */
	public void reset()
	{
		state = PollingThreadState.Waiting;
		_shutdown = false;
	}

	public IOPort getIoPort()
	{
		return ioPort;
	}

	public void setIoPort(IOPort ioPort)
	{
		this.ioPort = ioPort;
		if (transportMedium.platform != null)
			module = "Poll(" + transportMedium.platform.getSiteName(false) + ":" + ioPort.getPortNum() + ")";

	}

	public PollingThreadState getState()
	{
		return state;
	}

	public int getNumTries()
	{
		return numTries;
	}

	public TransportMedium getTransportMedium()
	{
		return transportMedium;
	}
	
	public void shutdown()
	{
		_shutdown = true;
		if (protocol != null)
			protocol.setAbnormalShutdown(new AbortException("PollingThread.shutdown()"));
	}

	public PlatformStatus getPlatformStatus()
	{
		return platformStatus;
	}

	public void setSaveSessionFile(String saveSessionFile)
	{
		this.saveSessionFile = saveSessionFile;
Logger.instance().debug3("setSaveSessionFile(" + saveSessionFile + ")");
	}

	public void setState(PollingThreadState state)
	{
		this.state = state;
	}
	
	public void info(String msg)
	{
		dataSource.log(Logger.E_INFORMATION, module + " " + msg);
		if (dacqEventLogger != null)
		{
			DacqEvent evt = new DacqEvent();
			evt.setPlatformId(transportMedium.platform.getId());
			evt.setSubsystem("Polling");
			evt.setEventPriority(Logger.E_INFORMATION);
			evt.setEventText(msg);
			dacqEventLogger.writeDacqEvent(evt);
		}
	}
	public void warning(String msg)
	{
		dataSource.log(Logger.E_WARNING, module + " " + msg);
		if (dacqEventLogger != null)
		{
			DacqEvent evt = new DacqEvent();
			evt.setPlatformId(transportMedium.platform.getId());
			evt.setSubsystem("Polling");
			evt.setEventPriority(Logger.E_WARNING);
			evt.setEventText(msg);
			dacqEventLogger.writeDacqEvent(evt);
		}
	}
	public void failure(String msg)
	{
		dataSource.log(Logger.E_FAILURE, module + " " + msg);
		if (dacqEventLogger != null)
		{
			DacqEvent evt = new DacqEvent();
			evt.setPlatformId(transportMedium.platform.getId());
			evt.setSubsystem("Polling");
			evt.setEventPriority(Logger.E_FAILURE);
			evt.setEventText(module + " " + msg);
			dacqEventLogger.writeDacqEvent(evt);
		}
	}
	public void debug1(String msg) { dataSource.log(Logger.E_DEBUG1, module + " " + msg); }
	public void debug2(String msg) { dataSource.log(Logger.E_DEBUG2, module + " " + msg); }
	public void debug3(String msg) { dataSource.log(Logger.E_DEBUG3, module + " " + msg); }
	
	public void annotate(String msg)
	{
		if (pollSessionLogger != null)
			pollSessionLogger.annotate(msg);
		else debug3("annotate(" + msg + ") pollSessionLogger is null.");
	}

	public String getModule()
	{
		return module;
	}

	public PollException getTerminatingException()
	{
		return terminatingException;
	}

	public int getPollPriority()
	{
		return pollPriority;
	}

	public Date getThreadStart()
	{
		return threadStart;
	}

	public void setThreadStart(Date threadStart)
	{
		this.threadStart = threadStart;
	}

	public void setMaxTries(int maxTries)
	{
		this.maxTries = maxTries;
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy