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

com.cosylab.epics.caj.impl.CABeaconHandler Maven / Gradle / Ivy

Go to download

JCA is an EPICS Channel Access library for Java. For more information concerning EPICS or Channel Access please refer to the <a href="http://www.aps.anl.gov/epics">EPICS Web pages</a> or read the <a href="http://www.aps.anl.gov/epics/base/R3-14/8-docs/CAref.html">Channel Access manual (3.14)</a>. <p>This module also includes CAJ, A 100% pure Java implementation of the EPICS Channel Access library.</p>

There is a newer version: 2.4.2
Show newest version
/*
 * Copyright (c) 2004 by Cosylab
 *
 * The full license specifying the redistribution, modification, usage and other
 * rights and obligations is included with the distribution of this project in
 * the file "LICENSE-CAJ". If the license is not included visit Cosylab web site,
 * .
 *
 * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE
 * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES
 * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION,
 * OR REDISTRIBUTION OF THIS SOFTWARE.
 */

package com.cosylab.epics.caj.impl;

import java.net.InetSocketAddress;
import java.util.logging.Level;

import com.cosylab.epics.caj.CAJContext;

/**
 * CA beacon handler.
 * @author Matej Sekoranja
 * @version $id$
 */
public class CABeaconHandler  {
	
	/**
	 * Context instance.
	 */
	private CAJContext context;

	/**
	 * Remote address.
	 */
	private InetSocketAddress responseFrom;

	/**
	 * Average period.
	 */
	private long averagePeriod = Long.MIN_VALUE;
	
	/**
	 * Period stabilization flag.
	 * If beacon monitoring began when server is being (re)started,
	 * beacon period increases by factor 2. This case is handled by this flag.
	 */
	private boolean periodStabilized = false;

	/**
	 * Last beacon sequence ID.
	 */
	private long lastBeaconSequenceID;
	
	/**
	 * Last beacon timestamp.
	 */
	private long lastBeaconTimeStamp = Long.MIN_VALUE;

	/**
	 * Transport initialization timestamp.
	 */
	private final long initializationTimeStamp = System.currentTimeMillis();

	/**
	 * Constructor.
	 * @param context context ot handle.
	 * @param responseFrom server to handle.
	 */
	public CABeaconHandler(CAJContext context, InetSocketAddress responseFrom)
	{
		this.context = context;
		this.responseFrom = responseFrom;

		context.getLogger().log(Level.FINE, () -> "CABeaconHandler for " + responseFrom + ": Speedup " + context.getBeaconSpeedup() + ", slowdown " + context.getBeaconSlowdown());
	}
	
	/**
	 * Update beacon period and do analitical checks (server restared, routing problems, etc.)
	 * @param remoteTransportRevision
	 * @param timestamp
	 * @param sequentalID
	 */
	public void beaconNotify(short remoteTransportRevision, long timestamp, long sequentalID)
	{
		context.getLogger().log(Level.FINE, () -> "Beacon " + timestamp + " [" + sequentalID + "] from " + responseFrom + " ...");
		boolean networkChanged = updateBeaconPeriod(remoteTransportRevision, timestamp, sequentalID);
		if (networkChanged)
			changedTransport();
	}

	/**
	 * Update beacon period.
	 * @param remoteTransportRevision
	 * @param timestamp
	 * @param sequentalID
	 * @return	network change (server restarted) detected.
	 */
	private synchronized boolean updateBeaconPeriod(short remoteTransportRevision, long timestamp, long sequentalID) {
		
		// first beacon notification check
		if (lastBeaconTimeStamp == Long.MIN_VALUE)
		{
			// new server up...
			context.getLogger().log(Level.INFO, () -> "New server beacon " + responseFrom);
			context.beaconAnomalyNotify();
			
			if (remoteTransportRevision >= 10)
			{
				lastBeaconSequenceID = sequentalID;
			}
			
			lastBeaconTimeStamp = timestamp;
			beaconArrivalNotify();
			return false;
		}

		// v4.10+ support beacon sequental IDs and additional checks are possible:
		// - detect beacon duplications due to redundant routes
		// - detect lost beacons due to input queue overrun or damage
		if (remoteTransportRevision >= 10)
		{
			long beaconSeqAdvance;
			if (sequentalID >= lastBeaconSequenceID)
				beaconSeqAdvance = sequentalID - lastBeaconSequenceID;
			else
				beaconSeqAdvance = (0x00000000FFFFFFFFL - lastBeaconSequenceID) + sequentalID;

			lastBeaconSequenceID = sequentalID;
			
			// throw out sequence numbers just prior to, or the same as, the last one received 
			// (this situation is probably caused by a temporary duplicate route )
			if (beaconSeqAdvance == 0 || beaconSeqAdvance > 0x00000000FFFFFFFFL - 256)
				return false;

			// throw out sequence numbers that jump forward by only a few numbers 
			// (this situation is probably caused by a duplicate route 
			//  or a beacon due to input queue overun)
			if (beaconSeqAdvance > 1 && beaconSeqAdvance < 4)
                        {
				// Ignore this beacon, but measure period of next beacon from this one
				lastBeaconTimeStamp = timestamp;
				return false;
                        }
		}

		boolean networkChange = false;
		long currentPeriod = timestamp - lastBeaconTimeStamp;

		// second beacon, period can be calculated now
		if (averagePeriod < 0)
		{
			averagePeriod = currentPeriod;

			beaconArrivalNotify();
		}
		else
		{
			// is this a server seen because of a restored network segment?
			if (currentPeriod >= (averagePeriod * context.getBeaconSlowdown()))
			{
				if (currentPeriod >= (averagePeriod * 3.25))
				{
					context.getLogger().log(Level.INFO, () -> "Restored network segment beacon " + responseFrom + ", period was " + averagePeriod + ", now " + currentPeriod);
					context.beaconAnomalyNotify();

					// trigger network change on any 3 contiguous missing beacons 
					networkChange = true;
				}
				else if (!periodStabilized)
				{
					// boost current period
					averagePeriod = currentPeriod;
					beaconArrivalNotify();
				}
				else
				{
					// something might be wrong...
					context.getLogger().log(Level.INFO, () -> "Delayed beacon " + responseFrom + ", period was " + averagePeriod + ", now " + currentPeriod);
					context.beaconAnomalyNotify();
				}
			}
			// is this a server seen because of reboot
			// (beacons come at a higher rate just after the)
			else if (periodStabilized  &&  currentPeriod <= (averagePeriod * context.getBeaconSpeedup()))
			{
				// server restarted...
				context.getLogger().log(Level.INFO, () -> "Fast 'reboot' beacon " + responseFrom + ", period was " + averagePeriod + ", now " + currentPeriod);
				context.beaconAnomalyNotify();
				
				networkChange = true;
			}
			// all OK, or still stabilizing
			else
			{
				periodStabilized = true;
				beaconArrivalNotify();
			}

			if (networkChange) {
				// reset 
				periodStabilized = false;
				averagePeriod = -1;
			}
			else {
				// update a running average period
				averagePeriod = (long)(currentPeriod * 0.125 + averagePeriod * 0.875);
			}
		}

		lastBeaconTimeStamp = timestamp;

		context.getLogger().log(Level.FINE, () -> "beacon " + responseFrom + " period: " + averagePeriod + (periodStabilized ? " (stable)" : " (not stable)"));
		
		return networkChange;
	}

	/**
	 * Notify transport about beacon arrival.
	 */
	private void beaconArrivalNotify()
	{
		Transport[] transports = context.getTransportRegistry().get(responseFrom);
		if (transports == null)
			return;

		// notify all
		for (int i = 0; i < transports.length; i++)
			((CATransport)transports[i]).beaconArrivalNotify();
	}


	/**
	 * Changed transport (server restared) notify. 
	 */
	private void changedTransport()
	{
		Transport[] transports = context.getTransportRegistry().get(responseFrom);
		if (transports == null)
			return;

		// notify all
		for (int i = 0; i < transports.length; i++)
			((CATransport)transports[i]).changedTransport();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy