001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2017 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029package edu.wisc.ssec.mcidasv.chooser.adde; 030 031import static javax.swing.GroupLayout.DEFAULT_SIZE; 032import static javax.swing.GroupLayout.PREFERRED_SIZE; 033import static javax.swing.GroupLayout.Alignment.BASELINE; 034import static javax.swing.GroupLayout.Alignment.LEADING; 035import static javax.swing.LayoutStyle.ComponentPlacement.RELATED; 036 037import java.awt.IllegalComponentStateException; 038import java.awt.Point; 039import java.awt.event.ActionEvent; 040import java.awt.event.ActionListener; 041import java.text.SimpleDateFormat; 042import java.util.ArrayList; 043import java.util.Arrays; 044import java.util.Collections; 045import java.util.Date; 046import java.util.Enumeration; 047import java.util.Hashtable; 048import java.util.List; 049import java.util.SortedSet; 050import java.util.TreeSet; 051 052import javax.swing.GroupLayout; 053import javax.swing.JButton; 054import javax.swing.JComboBox; 055import javax.swing.JComponent; 056import javax.swing.JDialog; 057import javax.swing.JLabel; 058import javax.swing.JPanel; 059import javax.swing.ListSelectionModel; 060 061import org.slf4j.Logger; 062import org.slf4j.LoggerFactory; 063import org.w3c.dom.Element; 064 065import edu.wisc.ssec.mcidas.McIDASUtil; 066import edu.wisc.ssec.mcidas.adde.AddePointDataReader; 067import edu.wisc.ssec.mcidas.adde.DataSetInfo; 068 069import ucar.unidata.data.DataSelection; 070import visad.DateTime; 071import visad.VisADException; 072 073import ucar.unidata.data.AddeUtil; 074import ucar.unidata.data.point.AddePointDataSource; 075import ucar.unidata.idv.chooser.IdvChooserManager; 076import ucar.unidata.idv.chooser.adde.AddeServer; 077import ucar.unidata.ui.DateTimePicker; 078import ucar.unidata.ui.symbol.StationModel; 079import ucar.unidata.ui.symbol.StationModelManager; 080import ucar.unidata.util.GuiUtils; 081import ucar.unidata.util.Misc; 082import ucar.unidata.util.TwoFacedObject; 083import ucar.visad.UtcDate; 084 085import edu.wisc.ssec.mcidasv.util.McVGuiUtils; 086import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width; 087 088/** 089 * Selection widget for ADDE point data 090 * 091 * @version $Revision$ $Date$ 092 */ 093public class AddePointDataChooser extends AddeChooser { 094 095 /** Logging object. Use it! */ 096 private static final Logger logger = LoggerFactory.getLogger(AddePointDataChooser.class); 097 098 /** 099 * Property for the dataset name key. 100 * @see edu.wisc.ssec.mcidasv.chooser.adde.AddeChooser#getDataSetName() 101 */ 102 public static String DATASET_NAME_KEY = "name"; 103 104 /** Property for the data type. */ 105 public static String DATA_TYPE = "ADDE.POINT.V"; 106 107 /** Are we currently reading times */ 108 private Object readTimesTask; 109 110 /** box and label for the relative time */ 111 protected JLabel relTimeIncLabel; 112 protected JComboBox relTimeIncBox; 113 114 /** the relative time increment */ 115 private float relativeTimeIncrement = 1.0f; 116 117 /** archive date */ 118 protected String archiveDay = null; 119 120 /** archive day button and label */ 121 protected JLabel archiveDayLabel; 122 protected JButton archiveDayBtn; 123 124 /** archive date formatter */ 125 private SimpleDateFormat archiveDayFormatter; 126 127 /** station model manager */ 128 private StationModelManager stationModelManager; 129 130 /** allowed descriptor prefix */ 131 protected String descriptorsAllowPrefix = ""; 132 133 protected boolean firstTime = true; 134 protected boolean retry = true; 135 136 /** Possibly ask for times a second time if the first sampling doesn't get any */ 137 private boolean gotObs = false; 138 protected boolean tryWithoutSampling = false; 139 140 /** 141 * Create a chooser for Adde POINT data 142 * 143 * @param mgr The chooser manager 144 * @param root The chooser.xml node 145 */ 146 public AddePointDataChooser(IdvChooserManager mgr, Element root) { 147 super(mgr, root); 148 149 this.stationModelManager = getIdv().getStationModelManager(); 150 151 relTimeIncLabel = new JLabel(" Interval:"); 152 relTimeIncBox = new JComboBox(); 153 relTimeIncBox.setToolTipText("Set the increment between relative times"); 154 relTimeIncBox.addActionListener(new ActionListener() { 155 @Override public void actionPerformed(ActionEvent ae) { 156 JComboBox box = (JComboBox) ae.getSource(); 157 if (GuiUtils.anySelected(box)) { 158 setRelativeTimeIncrement(getRelBoxValue()); 159 } 160 } 161 }); 162 McVGuiUtils.setComponentWidth(relTimeIncBox, Width.ONEHALF); 163 164 descriptorsAllowPrefix = ""; 165 166 archiveDayBtn = GuiUtils.makeImageButton( 167 "/auxdata/ui/icons/calendar_edit.png", this, "getArchiveDay", null, 168 true); 169 archiveDayBtn.setToolTipText("Select a day for archive datasets"); 170 archiveDayLabel = new JLabel("Select day:"); 171 archiveDayFormatter = new SimpleDateFormat(UtcDate.YMD_FORMAT); 172 } 173 174 /** 175 * Do server connection stuff... override this with type-specific methods 176 */ 177 @Override protected void readFromServer() { 178 archiveDay = null; 179 if (archiveDayLabel != null) { 180 archiveDayLabel.setText("Select day:"); 181 } 182 super.readFromServer(); 183 } 184 185 /** 186 * Generate a list of image descriptors for the descriptor list. 187 */ 188 @Override protected void readDescriptors() { 189 try { 190 StringBuffer buff = getGroupUrl(REQ_DATASETINFO, getGroup()); 191 buff.append("&type=").append(getDataType()); 192 DataSetInfo dsinfo = new DataSetInfo(buff.toString()); 193 descriptorTable = dsinfo.getDescriptionTable(); 194 195 // Only show descriptorsAllowPrefix if set 196 for (Enumeration e = descriptorTable.keys(); e.hasMoreElements();) { 197 Object key = e.nextElement(); 198 String str = (String)descriptorTable.get(key); 199 if (!descriptorsAllowPrefix.isEmpty() && str.indexOf(descriptorsAllowPrefix) != 0) { 200 descriptorTable.remove(key); 201 } 202 } 203 204 String[] names = new String[descriptorTable.size()]; 205 Enumeration enumeration = descriptorTable.keys(); 206 for (int i = 0; enumeration.hasMoreElements(); i++) { 207 Object thisElement = enumeration.nextElement(); 208 if (!isLocalServer()) { 209 names[i] = descriptorTable.get(thisElement).toString() + nameSeparator + thisElement.toString(); 210 } else { 211 names[i] = thisElement.toString(); 212 } 213 } 214 Arrays.sort(names); 215 setDescriptors(names); 216 setState(STATE_CONNECTED); 217 } catch (Exception e) { 218 handleConnectionError(e); 219 } 220 } 221 222 /** 223 * Load in an ADDE point data set based on the {@code PropertyChangeEvent}. 224 */ 225 @Override public void doLoadInThread() { 226 showWaitCursor(); 227 try { 228 StationModel selectedStationModel = getSelectedStationModel(); 229 String source = getRequestUrl(); 230 231 // make properties Hashtable to hand the station name 232 // to the AddeProfilerDataSource 233 Hashtable ht = new Hashtable(); 234 getDataSourceProperties(ht); 235 ht.put(DataSelection.PROP_CHOOSERTIMEMATCHING, getDoTimeDrivers()); 236 ht.put(AddePointDataSource.PROP_STATIONMODELNAME, 237 selectedStationModel.getName()); 238 ht.put(DATASET_NAME_KEY, getDescriptor()); 239 ht.put(DATA_NAME_KEY, getDataName()); 240 if (source.contains(AddeUtil.RELATIVE_TIME)) { 241 ht.put(AddeUtil.NUM_RELATIVE_TIMES, getRelativeTimeIndices()); 242 ht.put(AddeUtil.RELATIVE_TIME_INCREMENT, getRelativeTimeIncrement()); 243 } 244 245 if (getDoAbsoluteTimes()) { 246 ht.put(AddeUtil.ABSOLUTE_TIMES, getSelectedAbsoluteTimes()); 247 } 248 249 makeDataSource(source, DATA_TYPE, ht); 250 saveServerState(); 251 } catch (Exception excp) { 252 logException("Unable to open ADDE point dataset", excp); 253 } 254 showNormalCursor(); 255 // uncheck the check box every time click the add source button 256 drivercbx.setSelected(false); 257 enableTimeWidgets(); 258 setDoTimeDrivers(false); 259 } 260 261 /** 262 * Show the archive dialog. This method is not meant to be called but is 263 * public by reason of implementation (or insanity). 264 */ 265 public void getArchiveDay() { 266 final JDialog dialog = GuiUtils.createDialog("Set Archive Day", true); 267 final DateTimePicker dtp = new DateTimePicker((Date) null, false); 268 if (archiveDay != null) { 269 if (archiveDayFormatter == null) { 270 archiveDayFormatter = new SimpleDateFormat(UtcDate.YMD_FORMAT); 271 } 272 Date d = null; 273 try { 274 d = archiveDayFormatter.parse(archiveDay); 275 dtp.setDate(d); 276 } catch (Exception e) { 277 logException("parsing archive day " + archiveDay, e); 278 } 279 } 280 281 ActionListener listener = new ActionListener() { 282 public void actionPerformed(ActionEvent ae) { 283 String cmd = ae.getActionCommand(); 284 if (cmd.equals(GuiUtils.CMD_REMOVE)) { 285 archiveDay = null; 286 archiveDayLabel.setText("Select day:"); 287 setDoAbsoluteTimes(true); 288 descriptorChanged(); 289 } else if (cmd.equals(GuiUtils.CMD_OK)) { 290 try { 291 DateTime dt = new DateTime(dtp.getDate()); 292 String myDay = UtcDate.getYMD(dt); 293 // archiveDayLabel.setText(UtcDate.formatUtcDate(dt, 294 // "MMM dd, yyyy")); 295 archiveDayLabel.setText(myDay); 296 } catch (Exception e) { 297 logger.error("problem while setting archive day label", e); 298 } 299 // System.out.println("archiveDay = " + archiveDay); 300 setDoAbsoluteTimes(true); 301 descriptorChanged(); 302 } 303 dialog.dispose(); 304 } 305 }; 306 307 JPanel buttons = GuiUtils.makeButtons(listener, new String[] { 308 GuiUtils.CMD_OK, GuiUtils.CMD_REMOVE, GuiUtils.CMD_CANCEL }); 309 310 JComponent contents = GuiUtils.topCenterBottom(GuiUtils.inset(GuiUtils 311 .lLabel("Please select a day for this dataset:"), 10), GuiUtils 312 .inset(dtp, 10), buttons); 313 Point p = new Point(200, 200); 314 if (archiveDayBtn != null) { 315 try { 316 p = archiveDayBtn.getLocationOnScreen(); 317 } catch (IllegalComponentStateException ice) { 318 logger.error("archive day button in illegal state", ice); 319 } 320 } 321 dialog.setLocation(p); 322 dialog.getContentPane().add(contents); 323 dialog.pack(); 324 dialog.setVisible(true); 325 } 326 327 /** 328 * Get the selected station model. 329 * 330 * @return StationModel to use: defined by defaultModels list in ctor 331 */ 332 public StationModel getSelectedStationModel() { 333 StationModel returnModel = null; 334 if (isUpperAir()) { 335 returnModel = this.stationModelManager.getStationModel("Observations>Upper Air"); 336 } else if (isSynoptic()) { 337 returnModel = this.stationModelManager.getStationModel("Observations>SYNOP"); 338 } else { 339 returnModel = this.stationModelManager.getStationModel("Observations>METAR"); 340 } 341 return returnModel; 342 } 343 344 /** 345 * Add the interval selector to the component. 346 * @return superclass component with extra stuff 347 */ 348 @Override protected JPanel makeTimesPanel() { 349 JComponent extra1 = getExtraTimeComponentRelative(); 350 GuiUtils.enableTree(extra1, false); 351 JComponent extra2 = getExtraTimeComponentAbsolute(); 352 JPanel timesPanel = super.makeTimesPanel(extra1, extra2); 353 return timesPanel; 354 } 355 356 /** 357 * Get the extra time widget, but built in a different way. 358 * Designed to be put into a GroupLayout 359 * 360 * @return Extra time widget 361 */ 362 protected JComponent getExtraTimeComponentRelative() { 363 TwoFacedObject[] intervals = { 364 new TwoFacedObject("30 minute", .5f), 365 new TwoFacedObject("Hourly", 1f), 366 new TwoFacedObject("Three hourly", 3f), 367 new TwoFacedObject("Six hourly", 6f), 368 new TwoFacedObject("12 hourly", 12f), 369 new TwoFacedObject("24 hourly", 24f) 370 }; 371 372 GuiUtils.setListData(relTimeIncBox, intervals); 373 if (relTimeIncBox.getItemCount()>=2) relTimeIncBox.setSelectedIndex(1); 374 375 return McVGuiUtils.makeLabeledComponent(relTimeIncLabel, relTimeIncBox, McVGuiUtils.Position.LEFT); 376 } 377 378 /** 379 * Overridden in McIDAS-V to get a nicer set of interval combo box options. 380 * 381 * @return {@code JPanel} containing a label and the interval combo box. 382 */ 383 @Override protected JComponent getExtraRelativeTimeComponent() { 384 return getExtraTimeComponentRelative(); 385 } 386 387 /** 388 * Get the time popup widget 389 * 390 * @return a widget for selecing the day 391 */ 392 protected JComponent getExtraTimeComponentAbsolute() { 393 return null; 394// return McVGuiUtils.makeLabeledComponent(archiveDayLabel, archiveDayBtn); 395 } 396 397 /** 398 * Get the value from the relative increment box 399 * 400 * @return the selected value or a default 401 */ 402 protected float getRelBoxValue() { 403 float value = relativeTimeIncrement; 404 if (relTimeIncBox != null) { 405 Object o = relTimeIncBox.getSelectedItem(); 406 if (o != null) { 407 String val = TwoFacedObject.getIdString(o); 408 value = (float) Misc.parseNumber(val); 409 } 410 } 411 return value; 412 } 413 414 /** 415 * Get the string from the relative increment box 416 * 417 * @return the selected string or a default 418 */ 419 public String getRelBoxString() { 420 String value = ""; 421 if (relTimeIncBox != null) { 422 Object o = relTimeIncBox.getSelectedItem(); 423 if (o != null) { 424 value = TwoFacedObject.getIdString(o); 425 } 426 } 427 return value; 428 } 429 430 /** 431 * Get the request URL 432 * 433 * @return the request URL 434 */ 435 public String getRequestUrl() { 436 StringBuffer request = getGroupUrl(REQ_POINTDATA, getGroup()); 437 appendKeyValue(request, PROP_USER, getLastAddedUser()); 438 appendKeyValue(request, PROP_PROJ, getLastAddedProj()); 439 appendKeyValue(request, PROP_DESCR, getDescriptor()); 440 appendRequestSelectClause(request); 441 appendKeyValue(request, PROP_NUM, "ALL"); 442 appendKeyValue(request, PROP_POS, getDoRelativeTimes() ? "ALL" : "0"); 443 return request.toString(); 444 } 445 446 /** 447 * Get the select clause for the adde request specific to this 448 * type of data. 449 * 450 * @param buf The buffer to append to 451 */ 452 protected void appendRequestSelectClause(StringBuffer buf) { 453 StringBuilder selectValue = new StringBuilder(1024); 454 selectValue.append('\''); 455 selectValue.append(getDayTimeSelectString()); 456 //TODO: why is SFCHOURLY explicit here? better way to do it? 457 if ("SFCHOURLY".equalsIgnoreCase(getDescriptor())) { 458 selectValue.append(";type 0"); 459 } 460 selectValue.append(';'); 461 462 if (isUpperAir()){ 463 selectValue.append(AddeUtil.LEVEL); 464 selectValue.append(';'); 465 } 466 selectValue.append(AddeUtil.LATLON_BOX); 467 selectValue.append('\''); 468 appendKeyValue(buf, PROP_SELECT, selectValue.toString()); 469 } 470 471 /** 472 * Check if we are ready to read times 473 * 474 * @return true if times can be read 475 */ 476 protected boolean canReadTimes() { 477 return haveDescriptorSelected(); 478 } 479 480 /** 481 * Enable or disable the GUI widgets based on what has been 482 * selected. 483 */ 484 @Override protected void enableWidgets() { 485 boolean descriptorState = ((getState() == STATE_CONNECTED) 486 && canReadTimes()); 487 488 for (int i = 0; i < compsThatNeedDescriptor.size(); i++) { 489 JComponent comp = (JComponent) compsThatNeedDescriptor.get(i); 490 GuiUtils.enableTree(comp, descriptorState); 491 } 492 493 boolean timesOk = timesOk(); 494 495 // Require times to be selected 496 GuiUtils.enableTree(loadButton, descriptorState && timesOk); 497 498 checkTimesLists(); 499 500 enableAbsoluteTimesList(getDoAbsoluteTimes() && descriptorState); 501 502 getRelativeTimesChooser().setEnabled( !getDoAbsoluteTimes() 503 && descriptorState); 504 505 if (drivercbx != null) { 506// logger.trace("set drivercbx={}", anyTimeDrivers() && descriptorState); 507 drivercbx.setEnabled(anyTimeDrivers() && descriptorState); 508 } 509 510 revalidate(); 511 } 512 513 /** 514 * Do we have times selected. Either we are doing absolute 515 * times and there are some selected in the list. Or we 516 * are doing relative times and we have done a connect to the 517 * server 518 * 519 * @return Do we have times 520 */ 521 public boolean timesOk() { 522 if (getDoAbsoluteTimes() && !haveTimeSelected()) { 523 return false; 524 } 525 return true; 526 } 527 528 /** 529 * Return {@code true} if selected descriptor is for SYNOPTIC data. 530 * 531 * @return {@code true} iff {@link edu.wisc.ssec.mcidasv.chooser.adde.AddePointDataChooser#getDescriptor()} 532 * is {@literal "SYNOP"}. 533 */ 534 protected boolean isSynoptic() { 535 return "SYNOP".equals(getDescriptor()); 536 } 537 538 /** 539 * Return {@code true} if selected descriptor is for upper air. 540 * 541 * @return {@code true} iff {@link edu.wisc.ssec.mcidasv.chooser.adde.AddePointDataChooser#getDescriptor()} 542 * is {@literal "UPPERMAND"}. 543 */ 544 protected boolean isUpperAir() { 545 return "UPPERMAND".equals(getDescriptor()); 546 547 } 548 549 /** 550 * Return {@code true} if selected descriptor is for profiler. 551 * 552 * @return {@code true} iff {@link edu.wisc.ssec.mcidasv.chooser.adde.AddePointDataChooser#getDescriptor()} 553 * is {@literal "PROF"}. 554 */ 555 protected boolean isProfiler() { 556 return "PROF".equals(getDescriptor()); 557 } 558 559 /** 560 * Update the widget with the latest data. 561 * 562 * @throws Exception On badness 563 */ 564 @Override public void handleUpdate() throws Exception { 565 if (getState() != STATE_CONNECTED) { 566 //If not connected then update the server list 567 updateServerList(); 568 } else { 569 //If we are already connected then update the rest of the chooser 570 descriptorChanged(); 571 } 572 updateStatus(); 573 } 574 575 /** 576 * Get the request string for times particular to this chooser 577 * 578 * @return request string 579 */ 580 protected String getTimesRequest() { 581 StringBuffer buf = getGroupUrl(REQ_POINTDATA, getGroup()); 582 appendKeyValue(buf, PROP_USER, getLastAddedUser()); 583 appendKeyValue(buf, PROP_PROJ, getLastAddedProj()); 584 appendKeyValue(buf, PROP_DESCR, getDescriptor()); 585 if (!isUpperAir() && !tryWithoutSampling) { 586// appendKeyValue(buf, PROP_POS, "0"); 587 appendKeyValue(buf, PROP_POS, "ALL"); 588 appendKeyValue(buf, PROP_SELECT, "'DAY "+getJulianDay()+";LAT 38 42;LON 70 75'"); 589 } 590 else { 591 appendKeyValue(buf, PROP_SELECT, "'DAY "+getJulianDay()+"'"); 592 appendKeyValue(buf, PROP_POS, "ALL"); 593 } 594 if (getDoAbsoluteTimes()) { 595 appendKeyValue(buf, PROP_NUM, "ALL"); 596 } 597 appendKeyValue(buf, PROP_PARAM, "DAY TIME"); 598 return buf.toString(); 599 } 600 601 /** 602 * Get the current Julian day as a String 603 * 604 * @return the current day as a string (yyyyDDD) 605 */ 606 // TODO: This should really be more accessible 607 private String getJulianDay() { 608 String retString = ""; 609 try { 610 DateTime dt = new DateTime(); 611 retString = UtcDate.formatUtcDate(dt, "yyyyDDD"); 612 } catch (VisADException ve) { 613 logger.error("error building julian date", ve); 614 } 615 return retString; 616 } 617 618 /** 619 * This allows derived classes to provide their own name for labeling, etc. 620 * 621 * @return the dataset name 622 */ 623 @Override public String getDataName() { 624 return "Point Data"; 625 } 626 627 /** 628 * _more_ 629 */ 630 @Override public void doCancel() { 631 readTimesTask = null; 632 setState(STATE_UNCONNECTED); 633 super.doCancel(); 634 } 635 636 /** locking mutex */ 637 private final Object MUTEX = new Object(); 638 639 /** 640 * Read the set of image times available for the current server/group/type 641 * This method is a wrapper, setting the wait cursor and wrapping the 642 * call to {@link #readTimesInner()}; in a try/catch block 643 */ 644 @Override public void readTimes() { 645 clearTimesList(); 646 if (!canReadTimes()) { 647 return; 648 } 649 Misc.run(new Runnable() { 650 @Override public void run() { 651 updateStatus(); 652 showWaitCursor(); 653 try { 654 gotObs = false; 655 tryWithoutSampling = false; 656 readTimesInner(); 657 // Try again, this time not sampling by LAT/LON 658 if (haveDescriptorSelected() && !gotObs) { 659 tryWithoutSampling = true; 660 readTimesInner(); 661 } 662 } catch (Exception e) { 663 handleConnectionError(e); 664 } 665 showNormalCursor(); 666 updateStatus(); 667 } 668 }); 669 } 670 671 /** 672 * Set the list of dates/times based on the image selection 673 */ 674 protected void readTimesInner() { 675 SortedSet<DateTime> uniqueTimes = Collections.synchronizedSortedSet(new TreeSet<DateTime>()); 676 677 readTimesTask = startTask(); 678 updateStatus(); 679 Object task = readTimesTask; 680 try { 681 AddePointDataReader apr = new AddePointDataReader(getTimesRequest()); 682 //Make sure no other loads are occurred 683 boolean ok = stopTaskAndIsOk(task); 684 if (!Misc.equals(readTimesTask, task) || !ok) { 685 return; 686 } 687 readTimesTask = null; 688 689 synchronized (MUTEX) { 690 int[][] data = apr.getData(); 691 String[] units = apr.getUnits(); 692 if ( !"CYD".equals(units[0]) || !"HMS".equals(units[1])) { 693 throw new Exception("can't handle date/time units"); 694 } 695 int numObs = data[0].length; 696 //System.out.println("found " + numObs + " obs"); 697 // loop through and find all the unique times 698 try { 699 for (int i = 0; i < numObs; i++) { 700 DateTime dt = 701 new DateTime(McIDASUtil.mcDayTimeToSecs(data[0][i], 702 data[1][i])); 703 uniqueTimes.add(dt); 704 } 705 } catch (Exception e) { 706 logger.error("problem building list of unique times", e); 707 } 708 //System.out.println( 709 // "found " + uniqueTimes.size() + " unique times"); 710 if (getDoAbsoluteTimes()) { 711 if (!uniqueTimes.isEmpty()) { 712 setAbsoluteTimes(new ArrayList<DateTime>(uniqueTimes)); 713 getTimesList().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); 714 } 715 716 // Select the last n hours 717 int selectedIndex = getAbsoluteTimes().size() - 1; 718 int firstIndex = Math.max(0, selectedIndex 719 - getDefaultRelativeTimeIndex()); 720 if (selectedIndex >= 0) 721 setSelectedAbsoluteTime(selectedIndex, firstIndex); 722 } 723 if (numObs>0) { 724 gotObs = true; 725 } 726 } 727 setState(STATE_CONNECTED); 728 } catch (Exception excp) { 729 stopTask(task); 730 readTimesTask = null; 731 handleConnectionError(excp); 732 if (!retry) { 733 return; 734 } 735 try { 736 handleUpdate(); 737 } catch (Exception e) { 738 logger.error("problem handling update", e); 739 } 740 } 741 } 742 743 /** 744 * Show the given error to the user. If it was an Adde exception 745 * that was a bad server error then print out a nice message. 746 * 747 * @param e The exception 748 */ 749 @Override protected void handleConnectionError(Exception e) { 750 retry = false; 751 super.handleConnectionError(e); 752 } 753 754 /** 755 * Are there any times selected. 756 * 757 * @return Any times selected. 758 */ 759 @Override protected boolean haveTimeSelected() { 760 return !getDoAbsoluteTimes() || getHaveAbsoluteTimesSelected(); 761 } 762 763 /** 764 * Create the date time selection string for the "select" clause 765 * of the ADDE URL. 766 * 767 * @return the select day and time strings 768 */ 769 protected String getDayTimeSelectString() { 770 StringBuilder buf = new StringBuilder(1024); 771 if (getDoAbsoluteTimes()) { 772 List times = getSelectedAbsoluteTimes(); 773 774 // no time selection is permitted as a valid choice - 775 // will then use all times today by default. 776 if (times.isEmpty()) { 777 return ""; 778 } 779 780 //check for the "no times available" message 781 if (times.get(0) instanceof String) { 782 return ""; 783 } 784 785 if (archiveDay!=null) { 786 logger.trace("archiveDay: {}", archiveDay); 787 try { 788 Date d = archiveDayFormatter.parse(archiveDay); 789 DateTime dt = new DateTime(d); 790 logger.trace("parsed to: {}", dt.toString()); 791 buf.append("day ").append(UtcDate.getIYD(dt)).append(';'); 792 } catch (Exception e) { 793 logger.error("archiveDay parse error", e); 794 } 795 } 796 else { 797 logger.trace("archiveDay is null!"); 798 } 799 800 buf.append("time "); 801 for (int i = 0; i < times.size(); i++) { 802 DateTime dt = (DateTime) times.get(i); 803 buf.append(UtcDate.getHMS(dt)); 804 if (i != times.size() - 1) { 805 buf.append(','); 806 } 807 } 808 } else { 809 buf.append(AddeUtil.RELATIVE_TIME); 810 } 811 return buf.toString(); 812 } 813 814 /** 815 * Get the data type for this chooser 816 * 817 * @return the type 818 */ 819 @Override public String getDataType() { 820 return "POINT"; 821 } 822 823 /** 824 * Get the increment between times for relative time requests 825 * 826 * @return time increment (hours) 827 */ 828 @Override public float getRelativeTimeIncrement() { 829 return relativeTimeIncrement; 830 } 831 832 /** 833 * Set the increment between times for relative time requests 834 * 835 * @param increment time increment (hours) 836 */ 837 public void setRelativeTimeIncrement(float increment) { 838 relativeTimeIncrement = increment; 839 if (relTimeIncBox != null) { 840 relTimeIncBox.setSelectedItem(relativeTimeIncrement); 841 } 842 } 843 844 /** 845 * Update labels, enable widgets, etc. 846 */ 847 @Override protected void updateStatus() { 848 super.updateStatus(); 849 if (readTimesTask != null) { 850 if (taskOk(readTimesTask)) { 851 setStatus("Reading available times from server"); 852 } 853 } else if (getDoAbsoluteTimes() && !haveTimeSelected()) { 854 setStatus(MSG_TIMES); 855 } 856 enableWidgets(); 857 } 858 859 860 /** 861 * Get the descriptor widget label. 862 * 863 * @return label for the descriptor widget 864 */ 865 @Override public String getDescriptorLabel() { 866 return "Point Type"; 867 } 868 869 /** 870 * get the adde server grup type to use 871 * 872 * @return group type 873 */ 874 @Override protected String getGroupType() { 875 return AddeServer.TYPE_POINT; 876 } 877 878 /** 879 * Make the UI for this selector. 880 * 881 * @return The gui 882 */ 883 @Override public JComponent doMakeContents() { 884 JPanel myPanel = new JPanel(); 885 886 McVGuiUtils.setComponentWidth(descriptorComboBox, Width.DOUBLEDOUBLE); 887 888 JLabel stationLabel = McVGuiUtils.makeLabelRight("Station:"); 889 addServerComp(stationLabel); 890 891 JLabel timesLabel = McVGuiUtils.makeLabelRight("Times:"); 892 addDescComp(timesLabel); 893 894 JPanel timesPanel = makeTimesPanel(); 895 timesPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); 896 addDescComp(timesPanel); 897 898 GroupLayout layout = new GroupLayout(myPanel); 899 myPanel.setLayout(layout); 900 layout.setHorizontalGroup( 901 layout.createParallelGroup(LEADING) 902 .addGroup(layout.createSequentialGroup() 903 .addGroup(layout.createParallelGroup(LEADING) 904 .addGroup(layout.createSequentialGroup() 905 .addComponent(descriptorLabel) 906 .addGap(GAP_RELATED) 907 .addComponent(descriptorComboBox)) 908 .addGroup(layout.createSequentialGroup() 909 .addComponent(timesLabel) 910 .addGap(GAP_RELATED) 911 .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))) 912 ); 913 layout.setVerticalGroup( 914 layout.createParallelGroup(LEADING) 915 .addGroup(layout.createSequentialGroup() 916 .addGroup(layout.createParallelGroup(BASELINE) 917 .addComponent(descriptorLabel) 918 .addComponent(descriptorComboBox)) 919 .addPreferredGap(RELATED) 920 .addGroup(layout.createParallelGroup(LEADING) 921 .addComponent(timesLabel) 922 .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))) 923 ); 924 925 setInnerPanel(myPanel); 926 return super.doMakeContents(); 927 } 928 929 public JComponent doMakeContents(boolean doesOverride) { 930 if (doesOverride) { 931 return super.doMakeContents(); 932 } else { 933 return doMakeContents(); 934 } 935 } 936}