001    package edu.nrao.sss.model.resource.evla;
002    
003    import java.util.ArrayList;
004    import java.util.HashMap;
005    import java.util.HashSet;
006    import java.util.List;
007    import java.util.Map;
008    
009    import javax.xml.bind.annotation.XmlAttribute;
010    import javax.xml.bind.annotation.XmlElement;
011    
012    import edu.nrao.sss.model.resource.CorrelationProductGroup;
013    import edu.nrao.sss.model.resource.CorrelatorBaseband;
014    import edu.nrao.sss.model.resource.CorrelatorSubband;
015    
016    /**
017     * The complete collection of baseline board pairs for the WIDAR correlator.
018     * <p>
019     * <b>Version Info:</b>
020     * <table style="margin-left:2em">
021     *   <tr><td>$Revision: 2298 $</td></tr>
022     *   <tr><td>$Date: 2009-05-13 16:26:11 -0600 (Wed, 13 May 2009) $</td></tr>
023     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
024     * </table></p>
025     * 
026     * @author David M. Harland
027     * @since 2009-02-20
028     */
029    @javax.xml.bind.annotation.XmlRootElement
030    public class BlbpPool
031    {
032      private static final int WIDAR_BLBP_COUNT = 64;
033      
034      private EvlaWidarConfiguration poolOwner;
035      
036      //Key is UUID of WidarCorrelationProductGroup,
037      //value is # of BLBPs owned by that group.
038      private Map<String, Integer> blbpOwners;
039        
040      /**
041       * Creates a new pool for the given owner.
042       * @param ownerOfThisPool the owner of this pool
043       */
044      BlbpPool(EvlaWidarConfiguration ownerOfThisPool)
045      {
046        poolOwner  = ownerOfThisPool;
047        blbpOwners = new HashMap<String, Integer>();
048      }
049      
050      @SuppressWarnings("unused") //Used by JAXB and maybe Hibernate
051      private BlbpPool()  { this(null); }
052      
053      /** Intended for the sole use of <tt>EvlaWidarConfiguration</tt>. */
054      void setPoolOwner(EvlaWidarConfiguration ownerOfThisPool)
055      {
056        poolOwner = ownerOfThisPool;
057      }
058      
059      //============================================================================
060      // ALLOCATING & RECLAIMING BLBPs
061      //============================================================================
062    
063      /**
064       * Attempts to add the given number of BLBPs to {@code owner}.
065       * <p>
066       * The number of BLBPs allocated will be such that the total number of
067       * BLBPs owned by {@code owner} after allocation will be the largest
068       * valid number that is less than or equal to the desired new total.
069       * This is best explained via an example:</p>
070       * <blockquote>
071       *   Let the valid total number of boards for owner be in the set
072       *   {0, 4, 8, 12, 16}.  Let the currently owned number be 8.
073       *   If pairCount is 6, the desired new total is 14.  However, 14 is
074       *   not a valid value.  The largest valid number that is less than
075       *   or equal to 14 is 12, thus the value allocated, and returned by
076       *   this method, is 4, not 6.
077       * </blockquote>
078       * <p>
079       * There is an interesting side effect possible if the requested number
080       * is a value smaller than the increment between valid values:</p>
081       * <blockquote>
082       *   Using the set of valid totals from the previous example,
083       *   let the currently owned number be the illegal value of 6.
084       *   (This is possible when the properties of the owner change without
085       *   this pool's awareness.)
086       *   If pairCount is 1, the desired new total is 7.  However, 7 is
087       *   not a valid value.  The largest valid number that is less than
088       *   or equal to 7 is 4, thus the value allocated, and returned by
089       *   this method, is -2, not 1.
090       * </blockquote>
091       * 
092       * @param owner
093       *   the proud new owner of some baseline board pairs.
094       *   
095       * @param addlPairCount
096       *   the desired number of <i>additional</i> BLBPs to allocate to
097       *   {@code owner}. Negative values will be treated the same as a
098       *   value of zero.
099       *   
100       * @return
101       *   the number of additional pairs actually allocated.
102       *   This number could be less  than {@code pairCount} if this pool
103       *   is unable to meet the owner's request.
104       */
105      int allocateAdditionalPairsTo(WidarCorrelationProductGroup owner, int addlPairCount)
106      {
107        int allocated = 0;
108    
109        if (addlPairCount < 0)
110          addlPairCount = 0;
111        
112        int currentlyOwned  = getBlbpsOwnedBy(owner);
113        int newDesiredTotal = currentlyOwned + addlPairCount;
114    
115        List<Integer> validTotals = getValidBlbpCounts(owner);
116        
117        if (validTotals.contains(newDesiredTotal))
118        {
119          allocated = addlPairCount;
120        }
121        else
122        {
123          //Find largest value <= desired
124          for (int i=validTotals.size()-1; i >= 0; i--)
125          {
126            int validTot = validTotals.get(i);
127            if (validTot <= newDesiredTotal)
128              allocated = validTot - currentlyOwned;
129          }
130        }
131        
132        blbpOwners.put(owner.getUUID(), currentlyOwned + allocated);
133        
134        return allocated;
135      }
136    
137      /**
138       * Reclaims the given number of BLBPs currently held by {@code owner}.
139       * <p>
140       * The number of BLBPs reclaimed will be such that the total number of
141       * BLBPs owned by {@code owner} after reclamation will be the largest
142       * valid number that is less than or equal to the desired new total.
143       * This is best explained via an example:</p>
144       * <blockquote>
145       *   Let the valid total number of boards for owner be in the set
146       *   {0, 4, 8, 12, 16}.  Let the currently owned number be 12.
147       *   If pairCount is 5, the desired new total is 7.  However, 7 is
148       *   not a valid value.  The largest valid number that is less than
149       *   or equal to 7 is 4, thus the value reclaimed, and returned by
150       *   this method, is 8, not 5.
151       * </blockquote>
152       * <p>
153       * There is an interesting side effect possible if the requested number
154       * is a value smaller than the increment between valid values:</p>
155       * <blockquote>
156       *   Using the set of valid totals from the previous example,
157       *   let the currently owned number be the illegal value of 11.
158       *   (This is possible when the properties of the owner change without
159       *   this pool's awareness.)
160       *   If pairCount is 0, the desired new total is 11.  However, 11 is
161       *   not a valid value.  The largest valid number that is less than
162       *   or equal to 11 is 8, thus the value reclaimed, and returned by
163       *   this method, is 3, not 0.
164       * </blockquote>
165       * 
166       * @param owner
167       *   a current owner of baseline board pairs.
168       *   
169       * @param pairCount
170       *   the number of BLBPs to reclaim.
171       *   Negative values will be treated the same as a value of zero.
172       *   
173       * @return
174       *   the number of BLBPs reclaimed.  This number could be more
175       *   than {@code pairCount} if the desired new total is not a
176       *   valid number.
177       */
178      int reclaimPairsFrom(WidarCorrelationProductGroup owner, int pairCount)
179      {
180        int reclaimed = 0;
181        
182        if (pairCount <= 0)
183          pairCount = 0;
184        
185        int currentlyOwned  = getBlbpsOwnedBy(owner);
186        int newDesiredTotal = currentlyOwned - pairCount;
187    
188        if (newDesiredTotal <= 0)
189        {
190          reclaimed = reclaimPairsFrom(owner);
191        }
192        else
193        {
194          List<Integer> validTotals = getValidBlbpCounts(owner);
195          
196          if (validTotals.contains(newDesiredTotal))
197          {
198            reclaimed = pairCount;
199          }
200          else
201          {
202            //Find largest value <= desired
203            for (int i=validTotals.size()-1; i >= 0; i--)
204            {
205              int validTot = validTotals.get(i);
206              if (validTot <= newDesiredTotal)
207                reclaimed = currentlyOwned - validTot;
208            }
209          }
210          
211          int newTotal = currentlyOwned - reclaimed;
212          
213          if (newTotal <= 0)
214            blbpOwners.remove(owner.getUUID());
215          else
216            blbpOwners.put(owner.getUUID(), newTotal);
217        }
218        
219        return reclaimed;
220      }
221    
222      /**
223       * Reclaims all the BLBPs currently held by {@code owner}.
224       * 
225       * @param owner
226       *   a current owner of baseline board pairs.
227       *   
228       * @return
229       *   the number of BLBPs reclaimed.
230       */
231      int reclaimPairsFrom(WidarCorrelationProductGroup owner)
232      {
233        int reclaimed = getBlbpsOwnedBy(owner);
234    
235        blbpOwners.remove(owner.getUUID());
236        
237        return reclaimed;
238      }
239      
240      /**
241       * Compares the owners of the BLBPs to the set of valid potential owners
242       * and frees any fraudulently owned pairs.  The set of valid owners
243       * comes from the owner of this pool.  This method is useful when it
244       * appears that dead owners of BLBPs did not get a chance to release
245       * their BLBPs.
246       */
247      public void clean()
248      {
249        if (poolOwner != null)
250        {
251          HashSet<String> validOwners = new HashSet<String>();
252      
253          //Put together a set of valid owner IDs
254          for (CorrelatorBaseband bb : poolOwner.getBasebands())
255            for (CorrelatorSubband sb : bb.getSubbands())
256              for (CorrelationProductGroup cpg : sb.getCorrelationProductGroups())
257                validOwners.add(cpg.getUUID());
258          
259          //If a BLBP's owner is not in valid set, free it
260          for (String currOwner : blbpOwners.keySet())
261            if (!validOwners.contains(currOwner))
262              blbpOwners.remove(currOwner);
263        }
264    
265        //TODO perhaps this method should also make sure the current totals
266        //     are valid.  Beware: if 64 SBs all went from 4-bit to 7-bit
267        //     RQ, then pool is overallocated
268      }
269      
270      //============================================================================
271      // OWNERSHIP QUERIES
272      //============================================================================
273      
274      /**
275       * Returns a list of the number of BLBPs that may be owned by {@code owner}.
276       * Often the returned list will contain the integers zero through the number
277       * of unowned BLBPs.  However, depending on the properties of {@code owner},
278       * not all integral values in that range may be valid, and the maximum number
279       * might be something less than the total available.
280       * <p>
281       * The returned list is sorted from lowest to highest.</p>
282       * 
283       * @param owner
284       *   a potential owner of baseline board pairs.
285       *   
286       * @return
287       *   a list containing the valid numbers of BLBPs for the given owner.
288       */
289      List<Integer> getValidBlbpCounts(WidarCorrelationProductGroup owner)
290      {
291        List<Integer> valid = new ArrayList<Integer>();
292        
293        //TODO temp simple logic here
294        //  Need to:
295        //  1. DONE (was: add # owned by this group to total unowned)
296        //  2. consider amt owned by other groups in same baseband
297        //     Rem: if > 4 BLBPs for 1 SB, sacrifice SBOIDs
298        int incr = getBlbpIncrement(owner);
299        int max  = getBlbpsUnowned() + getBlbpsOwnedBy(owner);
300        for (int i = 0; i <= max; i += incr)
301          valid.add(i);
302        
303        return valid;
304      }
305      
306      /**
307       * Returns the smallest number of BLBPs that can be added to a subband
308       * that has the given requantization.
309       * 
310       * @param rqBits
311       *   the number of bits for the requantized signal.
312       *   
313       * @return
314       *   the BLBP increment for a subband with <tt>rqBits</tt>.
315       */
316      private int getBlbpIncr(int rqBits)
317      {
318        int incr = 1;
319        
320        if (rqBits == 7 && poolOwner != null && poolOwner.getMaxAntennaCount() > 16)
321          incr = 4;
322        
323        return incr;
324      }
325      
326      /**
327       * Returns the smallest number of BLBPs that should be added to <tt>owner's</tt>
328       * collection.  For most owners this value is <i>one</i>.  However, for owners
329       * who are part of a subband using 7-bit RQ and part of a configuration with
330       * more than 16 antennas, this value is <i>four</i>.
331       */
332      int getBlbpIncrement(WidarCorrelationProductGroup owner)
333      {
334        WidarSubband sb = owner.getSubband();
335        
336        return sb == null ? 1 : getBlbpIncr(sb.getRequantization());
337      }
338    
339      /**
340       * Returns the number of baseline board pairs owned by {@code owner}.
341       * 
342       * @param owner
343       *   a potential owner of baseline board pairs.
344       *   
345       * @return
346       *   the number of baseline board pairs owned by {@code owner}.
347       */
348      public int getBlbpsOwnedBy(WidarCorrelationProductGroup owner)
349      {
350        String key = owner.getUUID();
351        
352        return blbpOwners.containsKey(key) ? blbpOwners.get(key) : 0;
353      }
354      
355      /**
356       * Returns the number of baseline board pairs owned by subbands.
357       * @return the number of BLBPs owned by subbands
358       */
359      public int getBlbpsOwned()
360      {
361        clean();
362        
363        return tallyBlbpsOwned();
364      }
365      
366      private int tallyBlbpsOwned()
367      {
368        int owned = 0;
369        for (int i : blbpOwners.values())
370          owned += i;
371        
372        return owned;
373      }
374      
375      /**
376       * Returns the number of baseline boards pairs currently unowned.
377       * @return the free number of BLBPs.
378       */
379      public int getBlbpsUnowned()
380      {
381        return WIDAR_BLBP_COUNT - getBlbpsOwned(); 
382      }
383    
384      //============================================================================
385      // PERSISTENCE HELPERS
386      //============================================================================
387      
388      @XmlElement(name="owner")
389      @SuppressWarnings("unused") //Used by JAXB
390      private PoolMember[] getOwners()
391      {
392        clean();
393        
394        int valueCount = blbpOwners.size();
395        if (valueCount == 0)
396          return null;
397        
398        PoolMember[] result = new PoolMember[valueCount];
399        
400        int e=0;
401        for (Map.Entry<String, Integer> owner : blbpOwners.entrySet())
402          result[e++] = new PoolMember(owner.getKey(), owner.getValue());
403        
404        return result;
405      }
406      @SuppressWarnings("unused") //Used by JAXB
407      private void setOwners(PoolMember[] replacements)
408      {
409        blbpOwners.clear();
410        for (PoolMember pm : replacements)
411          blbpOwners.put(pm.id, pm.blbpCount);
412      }
413    
414      static class PoolMember
415      {
416        @XmlAttribute int blbpCount;
417        @XmlAttribute String id;
418        
419        PoolMember()  { this("",0); }
420        
421        PoolMember(String key, int value)
422        {
423          this.id        = key;
424          this.blbpCount = value;
425        }
426      }
427    
428      //============================================================================
429      // 
430      //============================================================================
431      /*
432      public static void main(String... args) throws Exception
433      {
434        BlbpPool pool = new BlbpPool();
435        
436        edu.nrao.sss.util.JaxbUtility jaxbUtil = new edu.nrao.sss.util.JaxbUtility();
437        javax.xml.validation.Schema   schema   = jaxbUtil.getSchemaFor(BlbpPool.class);
438        java.io.Writer                writer   = new java.io.PrintWriter(System.out);
439        
440        for (char c='a'; c <= 'm'; c++)
441          pool.blbpOwners.put(""+c, (int)(1+4*Math.random()));
442          
443        try
444        {
445          jaxbUtil.writeObjectAsXmlTo(writer, pool, schema);
446        }
447        catch (Exception ex)
448        {
449          System.out.println("Trouble w/ src.toXml.  Msg:");
450          System.out.println(ex.getMessage());
451          ex.printStackTrace();
452          
453          System.out.println("Attempting to write XML w/out schema verification:");
454          jaxbUtil.setLookForDefaultSchema(false);
455          try
456          {
457            jaxbUtil.writeObjectAsXmlTo(writer, pool, null);
458          }
459          catch (javax.xml.bind.JAXBException ex2)
460          {
461            System.out.println("Still had trouble w/ src.toXml.  Msg:");
462            System.out.println(ex.getMessage());
463            ex.printStackTrace();
464          }
465        }
466      }
467      */
468    }