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

decodes.cwms.CwmsTimeSeriesDb 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: CwmsTimeSeriesDb.java,v 1.57 2020/05/02 12:44:06 mmaloney Exp $
*
*  This is open-source software written by Sutron Corporation, under
*  contract to the federal government. You are free to copy and use this
*  source code for your own purposes, except that no part of the information
*  contained in this file may be claimed to be proprietary.
*
*  Except for specific contractual terms between ILEX and the federal
*  government, this source code is provided completely without warranty.
*  For more information contact: [email protected]
*
*  $Log: CwmsTimeSeriesDb.java,v $
*  Revision 1.57  2020/05/02 12:44:06  mmaloney
*  Add stack trace on connection failure.
*
*  Revision 1.56  2019/12/11 14:35:32  mmaloney
*  set module in ctor.
*
*  Revision 1.55  2019/07/02 13:53:26  mmaloney
*  Added flags2display
*
*  Revision 1.54  2019/06/10 19:22:17  mmaloney
*  Added getStorageUnitsForDataType
*
*  Revision 1.53  2019/03/15 11:58:54  mmaloney
*  dev
*
*  Revision 1.52  2019/02/25 20:02:55  mmaloney
*  HDB 660 Allow Computation Parameter Site and Datatype to be set independently in group comps.
*
*  Revision 1.51  2019/02/19 13:00:49  mmaloney
*  Add Michael Neilson's improvement for CWMS-14213
*
*  Revision 1.50  2019/01/29 16:45:17  mmaloney
*  dev
*
*  Revision 1.49  2019/01/18 15:06:48  mmaloney
*  dev
*
*  Revision 1.48  2019/01/11 14:39:17  mmaloney
*  Move JavaLoggerAdapter to ApplicationSettings
*
*  Revision 1.47  2019/01/03 15:04:55  mmaloney
*  changed prepareStatement to prepareCall
*
*  Revision 1.46  2018/12/21 17:26:21  mmaloney
*  dev
*
*  Revision 1.45  2018/12/21 17:09:01  mmaloney
*  dev
*
*  Revision 1.44  2018/12/21 17:00:45  mmaloney
*  dev
*
*  Revision 1.43  2018/12/20 21:25:11  mmaloney
*  dev
*
*  Revision 1.42  2018/12/20 15:52:21  mmaloney
*  dev
*
*  Revision 1.41  2018/12/18 20:48:52  mmaloney
*  dev
*
*  Revision 1.40  2018/12/18 16:15:06  mmaloney
*  Only capture specific loggers, otherwise you get tons of messages from X.
*
*  Revision 1.39  2018/12/18 15:21:02  mmaloney
*  Updates for jOOQ
*
*  Revision 1.38  2018/12/17 16:11:18  mmaloney
*  jOOQ Mods
*
*  Revision 1.37  2018/12/05 20:17:11  mmaloney
*  Use new connection pool to obtain connection.
*
*  Revision 1.36  2018/12/04 21:46:34  mmaloney
*  Removed unneeded import.
*
*  Revision 1.35  2018/11/28 21:18:48  mmaloney
*  CWMS JOOQ Migration Mods
*
*  Revision 1.34  2018/09/11 21:32:16  mmaloney
*  Modify morph() method to allow a way to create a mask that specifies to lop off the remainder of a parameter.
*
*  Revision 1.33  2018/05/23 19:59:01  mmaloney
*  OpenTSDB Initial Release
*
*  Revision 1.32  2018/05/01 17:34:13  mmaloney
*  Code cleanup
*
*  Revision 1.31  2018/02/21 14:34:19  mmaloney
*  Set autocommit true always.
*
*  Revision 1.30  2018/02/21 14:33:03  mmaloney
*  Set autocommit true always.
*
*  Revision 1.29  2018/02/19 16:22:30  mmaloney
*  Attempt to reclaim tasklist space if tasklist is empty and feature is enabled.
*
*  Revision 1.28  2018/02/14 17:02:37  mmaloney
*  CWMS-11849 Use prepared statement for the 2 queries that read the tasklist.
*
*  Revision 1.27  2017/11/14 21:50:11  mmaloney
*  Handle out of range ratings that return Const.UNDEFINED_DOUBLE.
*
*  Revision 1.26  2017/11/14 20:46:48  mmaloney
*  Handle out of range ratings that return Const.UNDEFINED_DOUBLE.
*
*  Revision 1.25  2017/08/22 19:31:18  mmaloney
*  Improve comments
*
*  Revision 1.24  2017/05/31 21:18:40  mmaloney
*  Added rating method to the TSDB object in order to remove dependencies to CWMS
*  from PythonAlgorithm.
*
*  Revision 1.23  2017/05/17 20:43:29  mmaloney
*  Remove ref to CwmsCatJdbc
*
*  Revision 1.22  2017/01/24 15:48:18  mmaloney
*  CWMS-9908 allow locationOverride to contain wildcard * char. Use same rules
*  as in the location part of the parameter mask.
*
*  Revision 1.21  2017/01/11 14:09:14  mmaloney
*  CompEdit CompParmDialog Lookup time Series should allow wildcards in the site name
*  for CWMS.
*
*  Revision 1.20  2017/01/10 21:46:11  mmaloney
*  Enhanced wildcard processing for CWMS as per punchlist for comp-depends project
*  for NWP.
*
*  Revision 1.19  2016/12/16 14:20:27  mmaloney
*  Enhanced resolver to allow triggering from a time series with unrelated location.
*
*  Revision 1.18  2016/11/29 00:56:02  mmaloney
*  Mods to transformUniqueString to handle wildcards.
*
*  Revision 1.17  2016/11/19 15:58:02  mmaloney
*  Support wildcards
*
*  Revision 1.16  2016/11/03 18:59:40  mmaloney
*  Implement wildcard evaluation for groups.
*
*  Revision 1.15  2016/08/13 17:40:31  mmaloney
*  DecodesSetting.cwmsVersionOverride to bypass Cwms 3 office privilege checks.
*
*  Revision 1.14  2016/08/05 14:43:37  mmaloney
*  cwmsVersionOverride to account for the fact that on some versions of CWMS there is no
*  reliable programmatic way to determine CWMS 2.1 vs CWMS 3.
*
*  Revision 1.13  2016/04/22 14:46:51  mmaloney
*  remove debug.
*
*  Revision 1.12  2016/03/24 19:00:45  mmaloney
*  Refactor: Have expandSDI return the TimeSeriesID that it uses. This saves the caller from
*  having to re-look it up. Needed for PythonAlgorithm.
*
*  Revision 1.11  2016/01/27 21:41:27  mmaloney
*  schedule_entry_status and dacq_event have their own sequences.
*
*  Revision 1.10  2015/09/10 21:18:29  mmaloney
*  Development on Screening
*
*  Revision 1.9  2015/05/14 13:52:19  mmaloney
*  RC08 prep
*
*  Revision 1.8  2015/04/02 18:10:03  mmaloney
*  Store dbURI and jdbcDriver so that CwmsConsumer can override them and
*  make them different from the definitions in DecodesSettings.
*  Fix bug in getNewDataSince whereby cache was getting refreshed on every
*  call rather than only once per hour.
*
*  Revision 1.7  2015/01/23 19:15:53  mmaloney
*  Improved debugs on CWMS qualcode in getNewData
*
*  Revision 1.6  2014/12/23 14:11:57  mmaloney
*  Explicitly reference hec.data.Units after connect to pre-initialize it.
*
*  Revision 1.5  2014/12/17 21:37:20  mmaloney
*  Failsafe check to make sure that tasklist record doesn't have null units.
*
*  Revision 1.4  2014/08/29 18:24:50  mmaloney
*  6.1 Schema Mods
*
*  Revision 1.3  2014/08/15 16:22:19  mmaloney
*  When reading tasklist, if mutliple recs for same time series with different units,
*  then convert subsequent recs to the same units as the first rec seen.
*
*  Revision 1.2  2014/05/22 12:17:15  mmaloney
*  Fix bug. After creating TS, set the site attribute of the tsid.
*
*  Revision 1.1.1.1  2014/05/19 15:28:59  mmaloney
*  OPENDCS 6.0 Initial Checkin
*
*  Revision 1.171  2013/08/06 14:02:49  mmaloney
*  tasklist queue bug fix.
*
*  Revision 1.170  2013/07/31 15:26:58  mmaloney
*  Added methods to check for questionable and set questionable.
*
*  Revision 1.169  2013/07/29 14:21:20  mmaloney
*  removed debugs
*
*  Revision 1.168  2013/07/24 18:11:27  mmaloney
*  dev
*
*  Revision 1.167  2013/07/24 13:35:20  mmaloney
*  Cleanup the listCompsForGui stuff. This is all now portable across databases.
*
*  Revision 1.166  2013/07/12 18:13:34  mmaloney
*  dev
*
*  Revision 1.165  2013/07/12 17:46:29  mmaloney
*  dev
*
*  Revision 1.164  2013/07/12 11:51:19  mmaloney
*  Added tasklist queue stuff.
*
*  Revision 1.163  2013/06/26 13:42:36  mmaloney
*  Implement CCP Privilege check.
*
*  Revision 1.162  2013/06/18 04:09:33  gchen
*  Modify the CWMS_CCP_VPD procedure name from set_ctx_db_office_id to set_session_office_id
*
*  Revision 1.161  2013/05/28 20:01:34  mmaloney
*  Fix for CWMS-3010
*
*  Revision 1.160  2013/04/26 15:07:59  mmaloney
*  dev
*
*  Revision 1.159  2013/04/26 15:07:03  mmaloney
*  Recover from bad login by allowing user to re-enter username/password.
*
*  Revision 1.158  2013/04/26 14:58:40  mmaloney
*  Dynamically determine CWMS version from TSDB Version.
*  TSDB Version 8 = CWMS Version 2.2
*
*  Revision 1.157  2013/04/24 23:49:28  mmaloney
*  dev
*
*  Revision 1.156  2013/04/23 13:25:23  mmaloney
*  Office ID filtering put back into Java.
*
*  Revision 1.155  2013/04/16 15:08:34  mmaloney
*  Use new createTs methods from Peter Morris.
*
*  Revision 1.154  2013/04/11 20:47:33  mmaloney
*  Update to call Gang's new setCtxUserId method
*
*  Revision 1.153  2013/04/11 12:48:11  gchen
*  *** empty log message ***
*
*  Revision 1.152  2013/04/11 12:13:17  gchen
*  Implement the setCtxDbOfficeId method to call the Oracle DB procedure cwms_ccp_vpd.set_ctx_db_office_code and to reset the VPD context variable with user specified office code.
*
*  Revision 1.151  2013/04/10 15:53:07  mmaloney
*  Stub for setting context office ID.
*
*  Revision 1.150  2013/04/08 17:57:46  mmaloney
*  determining privileged office IDs.
*
*  Revision 1.149  2013/04/04 19:24:56  mmaloney
*  CWMS connection stuff for both DECODES and TSDB.
*
*  Revision 1.148  2013/04/01 22:15:34  mmaloney
*  dev
*
*  Revision 1.147  2013/03/27 18:42:29  mmaloney
*  CWMS 2.2 Mods
*
*  Revision 1.146  2013/03/22 20:02:15  mmaloney
*  Added DST versions of 3, 4, 6, 8, and 12 hours.
*
*  Revision 1.145  2013/03/21 18:27:40  mmaloney
*  DbKey Implementation
*
*  Revision 1.144  2013/02/20 15:07:24  gchen
*  Enhance a new feature to allow to use the maxComputationRetries property
*  to limit the number of retries for those failed computations. There will
*  be unlimited retries if maxComputationRetires=0.
*
*  Revision 1.143  2012/11/13 15:14:49  mmaloney
*  dev
*
*  Revision 1.142  2012/10/22 14:31:02  mmaloney
*  User upper() on officeID
*
*  Revision 1.141  2012/10/09 12:42:03  mmaloney
*  removed old debugs.
*
*  Revision 1.140  2012/10/02 18:04:28  mmaloney
*  Don't set display name on default CwmsTsId constructor.
*
*  Revision 1.139  2012/09/17 21:13:28  mmaloney
*  fillTimeSeriesMetadat should call CTimeSeries.setTimeSeriesIdentifier if necessary.
*
*  Revision 1.138  2012/09/12 21:32:17  mmaloney
*  Get full site when getting TSID.
*
*  Revision 1.137  2012/09/12 21:15:14  mmaloney
*  use site cache.
*  In setParmSDI, fill in all site names.
*
*  Revision 1.136  2012/09/11 13:03:16  mmaloney
*  dev
*
*  Revision 1.135  2012/09/11 12:57:25  mmaloney
*  dev
*
*  Revision 1.134  2012/09/11 12:40:41  mmaloney
*  set display name.
*
*  Revision 1.133  2012/09/11 12:34:25  mmaloney
*  debug
*
*  Revision 1.132  2012/09/11 00:51:17  mmaloney
*  dev
*
*  Revision 1.131  2012/09/11 00:46:31  mmaloney
*  Add parenthesized display name capability.
*
*  Revision 1.130  2012/09/10 18:57:04  mmaloney
*  If cancel on login dialog, don't try to connect to DB.
*
*  Revision 1.129  2012/08/13 15:21:10  mmaloney
*  added makeEmptyTsId().
*
*  Revision 1.128  2012/07/30 13:22:22  mmaloney
*  code cleanup.
*
*  Revision 1.127  2012/07/24 14:30:04  mmaloney
*  getDataTypesByStandard moved to base class decodes.tsdb.TimeSeriesDb.
*
*  Revision 1.126  2012/07/23 15:20:44  mmaloney
*  Refactor group evaluation for HDB.
*
*  Revision 1.125  2012/07/15 19:54:10  mmaloney
*  Refactor read/write DateFmt. For HDB, always use GMT/UTC.
*
*  Revision 1.124  2012/07/12 19:24:58  mmaloney
*  timestamp debugs.
*
*  Revision 1.123  2012/07/12 17:23:07  mmaloney
*  debugDateFmt when saving ts data.
*
*  Revision 1.122  2012/07/05 18:24:02  mmaloney
*  CWMS location names may contain spaces and multiple hyphens.
*  ts key is stored as a long.
*
*  Revision 1.121  2012/06/18 15:14:55  mmaloney
*  Moved TS ID cache to base class.
*
*  Revision 1.120  2012/06/18 13:20:43  mmaloney
*  minRecNum in cp_comp_tasklist must be defined as long
*
*  Revision 1.119  2012/06/06 15:15:34  mmaloney
*  reduced a debug
*
*  Revision 1.118  2012/05/25 17:22:53  mmaloney
*  Omaha problem with protected-flag.
*
*  Revision 1.117  2012/05/17 15:09:37  mmaloney
*  Clean up imports and remove proprietary dependencies from OS code.
*
*  Revision 1.116  2012/05/15 14:28:14  mmaloney
*  1. createTimeSeries calls checkValid, which can throw BadTimeSeriesException.
*  2. transformTsidByCompParm can throw BadTimeSeriesException because
*  it calls createTimeSeries if create flag == true.
*
*  Revision 1.115  2012/05/09 21:34:00  mmaloney
*  Read CWMS quality_code as a long int.
*
*  Revision 1.114  2012/04/30 18:52:11  mmaloney
*  fillDependentCompIds must filter on loadingAppId.
*
*  Revision 1.113  2012/02/27 16:55:50  gchen
*  Modified the getNewDataSince() function to separate the tasklist table records with 'Y' and "N' delete flag, which would temporarily solved the failed computation issues while using Resample and Subsample algorithms.
*
*  Revision 1.112  2012/01/26 17:40:03  mmaloney
*  Check for nul return on quality code.
*
*  Revision 1.111  2012/01/17 17:43:50  mmaloney
*  Call DecodesInterface.setGUI(true).
*
*  Revision 1.110  2011/12/16 20:25:09  mmaloney
*  dev
*
*  Revision 1.109  2011/12/16 20:21:06  mmaloney
*  GUI Apps must login via dialog.
*
*  Revision 1.108  2011/11/07 21:56:10  mmaloney
*  Check for INFINITE when reading CWMS_V_TSV, otherwise, the ROUND method throws a SQL overflow error.
*
*  Revision 1.107  2011/10/19 22:21:12  gchen
*  Modify the CwmsTimeSeriesDb with adding the writeTsGroup() to avoid using the one in TimeSeriesDb. This will solve the issue that cp_comp_depends gets updated while a TS group is saved into CWMS DB.
*
*  Revision 1.106  2011/09/21 19:19:22  gchen
*  Fix the bug in using XML as DECODES DB against CWMS DB (no CCP DB objects).
*
*  Revision 1.105  2011/09/09 06:42:16  mmaloney
*  For CWMS Consumer, don't fail connect if the CCP tables don't exist.
*
*  Revision 1.104  2011/09/01 21:34:37  mmaloney
*  dev
*
*  Revision 1.103  2011/07/12 18:55:14  mmaloney
*  dev
*
*  Revision 1.102  2011/06/17 13:31:22  gchen
*  Fix the null pointer exception when starting the chain computations
*
*  Revision 1.101  2011/06/16 18:15:40  mmaloney
*  Bad quality code logic in getprev & get next.
*
*  Revision 1.100  2011/06/16 15:25:38  mmaloney
*  dev
*
*  Revision 1.99  2011/06/16 14:12:59  mmaloney
*  dev
*
*  Revision 1.98  2011/06/16 14:07:25  mmaloney
*  fix
*
*  Revision 1.97  2011/06/16 13:30:24  mmaloney
*  Initialize & refresh tsid cache every hour.
*
*  Revision 1.96  2011/05/16 13:56:28  mmaloney
*  Allow 300 dates per query in fillTimeSeries
*
*  Revision 1.95  2011/05/03 16:37:57  mmaloney
*  Optimize fillTimeSeriesMetadata - read meta-data out of the cache.
*
*  Revision 1.94  2011/04/27 19:12:03  mmaloney
*  rs2TimedVariable: DO return REJECTED values. Only MISSING values are discarded.
*
*  Revision 1.93  2011/04/19 19:14:10  gchen
*  (1) Add a line to set Site.explicitList = true in cwmsTimeSeriesDb.java to fix the multiple location entries on Location Selector in TS Group GUI.
*
*  (2) Fix a bug in getDataType(String standard, String code, int id) method in decodes.db.DataType.java because the data id wasn't set up previously.
*
*  (3) Fix the null point exception in line 154 in cwmsGroupHelper.java.
*
*  Revision 1.92  2011/04/14 15:52:10  mmaloney
*  dev
*
*  Revision 1.91  2011/04/14 15:47:04  mmaloney
*  dev
*
*  Revision 1.90  2011/04/14 15:17:37  mmaloney
*  In comprun gui show alpha codes SQRM, rather than hex.
*
*  Revision 1.89  2011/03/24 19:10:20  mmaloney
*  Added codes for tilde-intervals for CWMS
*
*  Revision 1.88  2011/03/23 15:19:53  mmaloney
*  expandSDI use the TimeSeriesId method (& cache) to avoid unnecessary db reads.
*
*  Revision 1.87  2011/03/22 17:34:39  mmaloney
*  Instantiate cwms tsid cache.
*
*  Revision 1.86  2011/03/22 16:45:27  mmaloney
*  caching improvements.
*
*  Revision 1.85  2011/03/22 14:13:25  mmaloney
*  Added caching for DbComputations and CWMS Time Series Identifiers.
*
*  Revision 1.84  2011/03/18 14:48:42  mmaloney
*  bug in listTimeSeries
*
*  Revision 1.83  2011/03/18 14:15:45  mmaloney
*  transform tsid method split off.
*
*  Revision 1.82  2011/03/17 15:21:56  mmaloney
*  bugfix
*
*  Revision 1.81  2011/03/17 14:55:58  mmaloney
*  When reading tsid, DON'T join with the datatype table. Do that in separate step.
*
*  Revision 1.80  2011/03/04 20:39:55  mmaloney
*  created
*
*  Revision 1.79  2011/03/03 13:45:08  mmaloney
*  Code must not assume that decodes db is the same as CWMS db. We must accommodate
*  districts that are using XML decodes.
*
*  Revision 1.78  2011/03/01 15:55:30  mmaloney
*  Implement deleteTimeSeries
*
*  Revision 1.77  2011/02/18 14:42:22  mmaloney
*  Don't put null TimedVariables into CTimeSeries.
*
*  Revision 1.76  2011/02/17 13:50:35  mmaloney
*  When creating time-series, use the correct HecConstant for UTC Offset.
*
*  Revision 1.75  2011/02/17 13:38:17  mmaloney
*  Guard against overflow exception when reading time-series values.
*
*  Revision 1.74  2011/02/17 13:05:13  mmaloney
*  Must retrieve CWMS quality code as a BigDecimal and then convert to long.
*
*  Revision 1.73  2011/02/16 14:56:47  mmaloney
*  Timeout mechanism for re-subscribing to the CWMS queue.
*
*  Revision 1.72  2011/02/15 17:00:01  mmaloney
*  sql syntax bug fix.
*
*  Revision 1.71  2011/02/15 16:52:34  mmaloney
*  Defensive programming: Don't join the task list with any other tables because the
*  join might fail, leaving bogus tasklist entries on the queue forever.
*
*  Revision 1.70  2011/02/11 20:44:13  mmaloney
*  Do not through DbIoException when call to store fails. CWMS has so many business rules
*  that can cause a store to fail. It does not mean the connection is bad.
*
*  Revision 1.69  2011/02/11 20:13:00  mmaloney
*  Only receive 10,000 records at a time in getNewData.
*
*  Revision 1.68  2011/02/11 18:54:51  mmaloney
*  Only receive 10,000 records at a time in getNewData.
*
*  Revision 1.67  2011/02/08 13:29:27  mmaloney
*  All tsdb reads must be units-savvy.
*
*  Revision 1.66  2011/02/08 00:24:25  mmaloney
*  MUST convert units on output so that the store_ts hook will store units in the
*  database-storage units. Otherwise compproc will have the wrong units.
*
*  Revision 1.65  2011/02/08 00:11:58  mmaloney
*  MUST convert units on output so that the store_ts hook will store units in the
*  database-storage units. Otherwise compproc will have the wrong units.
*
*  Revision 1.64  2011/02/08 00:03:47  mmaloney
*  MUST convert units on output so that the store_ts hook will store units in the
*  database-storage units. Otherwise compproc will have the wrong units.
*
*  Revision 1.63  2011/02/07 23:52:09  mmaloney
*  MUST convert units on output so that the store_ts hook will store units in the
*  database-storage units. Otherwise compproc will have the wrong units.
*
*  Revision 1.62  2011/02/07 18:34:34  mmaloney
*  Got rid of PgTimeSeriesDb intermediate class.
*  Revision 1.61  2011/02/03 20:00:23  mmaloney
*  Time Series Group Editor Mods
*
*  Revision 1.60  2011/02/02 20:42:28  mmaloney
*  bug fixes
*
*  Revision 1.59  2011/02/02 14:32:30  mmaloney
*  debugs
*
*  Revision 1.58  2011/02/01 15:31:10  gchen
*  *** empty log message ***
*
*  Revision 1.57  2011/01/31 15:21:29  mmaloney
*  debug
*
*  Revision 1.56  2011/01/31 14:03:04  mmaloney
*  For Cwms, where datatype can be any odd combination of Param-SubParam,
*  when importing a computation, if datatype doesn't already exist, create it.
*
*  Revision 1.55  2011/01/28 20:01:46  gchen
*  *** empty log message ***
*
*  Revision 1.54  2011/01/27 23:27:32  gchen
*  Add the listTimeSeries method to retrieve TS Id data
*
*  Revision 1.53  2011/01/26 21:34:08  mmaloney
*  Check for null TV
*
*  Revision 1.52  2011/01/24 18:59:02  mmaloney
*  DataType.getDataType was being called with arguments swapped.
*
*  Revision 1.51  2011/01/21 20:39:49  mmaloney
*  With new store method, must never put a null value in the value array.
*
*  Revision 1.50  2011/01/21 20:30:36  mmaloney
*  debug
*
*  Revision 1.49  2011/01/21 20:20:00  mmaloney
*  debug
*
*  Revision 1.48  2011/01/20 13:09:17  mmaloney
*  Added stub for listTimeSeries
*
*  Revision 1.47  2011/01/19 15:44:30  mmaloney
*  Set display name to unique string only.
*
*  Revision 1.46  2011/01/18 19:20:48  mmaloney
*  dev
*
*  Revision 1.45  2011/01/13 13:04:44  mmaloney
*  Call refresh_mv_cwms_ts_id after creating a time series in the database.
*
*  Revision 1.44  2011/01/13 12:28:24  mmaloney
*  fix getNewData
*
*  Revision 1.43  2011/01/12 22:06:12  mmaloney
*  getNewData must create CwmsTsIds for each time series returned.
*
*  Revision 1.42  2011/01/12 21:46:38  mmaloney
*  dev
*
*  Revision 1.41  2011/01/12 20:59:03  mmaloney
*  dev
*
*  Revision 1.40  2011/01/12 20:49:30  mmaloney
*  dev
*
*  Revision 1.39  2011/01/11 18:46:06  mmaloney
*  dev
*
*  Revision 1.38  2011/01/10 20:57:16  mmaloney
*  dev
*
*  Revision 1.37  2011/01/10 20:43:19  mmaloney
*  connect issues
*
*  Revision 1.36  2011/01/10 20:25:58  mmaloney
*  debug
*
*  Revision 1.35  2011/01/10 19:32:08  mmaloney
*  Don't overload methods to evaluate cp_comp_depends. This is all done in the base class now.
*
*  Revision 1.34  2011/01/10 18:48:57  mmaloney
*  dataType ID is NOT the same as parameter_code. We keep our own copy of the CWMS parameter ids in our datatype table. The ID refers to our own table.
*
*  Revision 1.33  2011/01/10 18:24:27  mmaloney
*  Removed lookupDataType and getDataTypeById from the CWMS sub-class.
*
*  Revision 1.32  2011/01/05 21:03:21  mmaloney
*  bugfix
*
*  Revision 1.31  2011/01/05 20:50:38  mmaloney
*  bugfix
*
*  Revision 1.30  2011/01/05 13:25:17  mmaloney
*  dev
*
*  Revision 1.29  2011/01/01 21:28:53  mmaloney
*  CWMS Testing
*
*  Revision 1.28  2010/12/21 19:31:04  mmaloney
*  group computations
*
*  Revision 1.27  2010/12/05 15:48:10  mmaloney
*  Cleanup and creation of HecConstants.
*
*  Revision 1.26  2010/11/28 21:05:25  mmaloney
*  Refactoring for CCP Time-Series Groups
*
*  Revision 1.25  2010/11/05 18:19:59  mmaloney
*  Use RMA methods for writing time-series data and sites.
*
*  Revision 1.24  2010/10/29 15:08:23  mmaloney
*  Several CWMS updates.
*
*  Revision 1.23  2010/10/22 18:03:32  mmaloney
*  CCP Refactoring
*
*  Revision 1.22  2010/09/30 19:01:15  mmaloney
*  Moved determineTsdbVersion to base class TimeSeriesDb
*
*  Revision 1.21  2010/08/20 19:24:37  mmaloney
*  Added CVS Log to header
*
*/
package decodes.cwms;

import java.util.ArrayList;
import java.util.Properties;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.GregorianCalendar;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.TimeZone;

import javax.management.JMException;
import javax.management.ObjectName;
import javax.management.openmbean.TabularData;

import org.opendcs.database.ExceptionHelpers;
import org.opendcs.jmx.ConnectionPoolMXBean;
import org.opendcs.utils.FailableResult;
import org.slf4j.LoggerFactory;

import opendcs.dai.DaiBase;
import opendcs.dai.DataTypeDAI;
import opendcs.dai.IntervalDAI;
import opendcs.dai.LoadingAppDAI;
import opendcs.dai.ScheduleEntryDAI;
import opendcs.dai.SiteDAI;
import opendcs.dai.TimeSeriesDAI;
import opendcs.dao.DaoBase;
import opendcs.dao.DaoHelper;
import opendcs.dao.LoadingAppDao;
import opendcs.opentsdb.OpenTsdbSettings;
import opendcs.util.sql.WrappedConnection;
import lrgs.gui.DecodesInterface;

import java.sql.PreparedStatement;

import usace.cwms.db.dao.ifc.sec.CwmsDbSec;
import usace.cwms.db.dao.util.connection.ConnectionLoginInfo;
import usace.cwms.db.dao.util.connection.ConnectionLoginInfoImpl;
import usace.cwms.db.dao.util.connection.CwmsDbConnectionPool;
import usace.cwms.db.dao.util.services.CwmsDbServiceLookup;
import hec.data.RatingException;
import hec.data.cwmsRating.RatingSet;
import hec.lang.Const;
import ilex.util.Logger;
import ilex.util.StringPair;
import ilex.util.TextUtil;
import ilex.var.NamedVariable;
import ilex.var.Variable;
import decodes.cwms.rating.CwmsRatingDao;
import decodes.cwms.validation.dao.ScreeningDAI;
import decodes.cwms.validation.dao.ScreeningDAO;
import decodes.db.Constants;
import decodes.db.Site;
import decodes.db.SiteName;
import decodes.db.DataType;
import decodes.sql.DbKey;
import decodes.sql.DecodesDatabaseVersion;
import decodes.sql.OracleSequenceKeyGenerator;
import decodes.tsdb.*;
import decodes.util.DecodesSettings;

/**
This is the base class for the time-series database implementation.
Sub classes must override all the abstract methods and provide
a mechanism to persistently store time series and computational meta
data.
*/
public class CwmsTimeSeriesDb
	extends TimeSeriesDb
{
	private static final org.slf4j.Logger log = LoggerFactory.getLogger(CwmsTimeSeriesDb.class);
	private CwmsConnectionPool pool = null;

	private String dbOfficeId = null;
	private DbKey dbOfficeCode = null;

	private String[] currentlyUsedVersions = { "" };
	GregorianCalendar saveTsCal = new GregorianCalendar(
		TimeZone.getTimeZone("UTC"));

	CwmsGroupHelper cwmsGroupHelper = null;

	public boolean requireCcpTables = true;

	private String dbUri = null;

	private BaseParam baseParam = new BaseParam();
	private PreparedStatement getMinStmt = null, getTaskListStmt;
	String getMinStmtQuery = null, getTaskListStmtQuery = null;
	
	/** Set after first connect, reused by getConnection() called from DAOs */
	private CwmsConnectionInfo conInfo = null;

	/**
	 * No args constructor required because this is instantiated from
	 * the class name.
	 */
	public CwmsTimeSeriesDb()
	{
		super();

		Site.explicitList = true;

		// CWMS uses ts_code as a unique identifier of a time-series
		// Internally our SDI (site datatype id) is equivalent to CWMS ts_code
		sdiIsUnique = true;

		curTimeName = "sysdate";
		maxCompRetryTimeFrmt = "%d*1/24";
		module = "CwmsTimeSeriesDb";
		
	}

	public static Connection getDbConnection(final CwmsConnectionInfo info) throws BadConnectException
	{
		try
		{
			Connection conn = CwmsConnectionPool.getPoolFor(info).getConnection();
			return conn;
		}
		catch(SQLException ex)
		{
			throw new BadConnectException("Unable to get connection from pool",ex);
		}
	}
	

	/**
	 * Connect this app to the database and return appID.
	 * The credentials property set contains username, password,
	 * etc, for connecting to database.
	 * @param appName must match an application in the database.
	 * @param credentials must contain all needed login parameters.
	 * @return application ID.
	 * @throws BadConnectException if failure to connect.
	 */
	public DbKey connect( String appName, Properties credentials )
		throws BadConnectException
	{
		String dbUri = this.dbUri != null ? this.dbUri : DecodesSettings.instance().editDatabaseLocation;

		String username = null;
		String password = null;
		CwmsGuiLogin cgl = null;
		if (credentials == null && DecodesInterface.isGUI())
		{			
			cgl = CwmsGuiLogin.instance();
			try
			{
				if (!cgl.isLoginSuccess())
				{
					cgl.doLogin(null);
					if (!cgl.wasOk()) // user hit cancel
					{
						throw new BadConnectException("Login aborted by user.");
					}
				}
				username = cgl.getUserName();
				password = new String(cgl.getPassword());
			}
			catch(Exception ex)
			{
				throw new BadConnectException(
					"Cannot display login dialog: " + ex);
			}
		}
		else if(credentials == null)
		{
			throw new BadConnectException("Cannot connect to CWMS without credentials.");
		}
		else
		{	// use the provided credentials
			username = credentials.getProperty("username");
			password = credentials.getProperty("password");
		}

		// MJM 2018-12-05 The new HEC/RMA connection facility requires that office ID
		// be known before getting a connection from the pool. Therefore I cannot set
		// it dynamically from the database or from user selection.
		dbOfficeId = DecodesSettings.instance().CwmsOfficeId;

		// CWMS is Always GMT.
		DecodesSettings.instance().sqlTimeZone = "GMT";


		if (conInfo == null )
		{
			conInfo = new CwmsConnectionInfo();		
			ConnectionLoginInfo loginInfo = new ConnectionLoginInfoImpl(dbUri, username, password, dbOfficeId);			
			conInfo.setLoginInfo(loginInfo);
		}

		pool = CwmsConnectionPool.getPoolFor(conInfo);
		if( pool != null)
		{
			try(Connection conn = pool.getConnection();)
			{
					Logger.instance().info(module +
				" Connected to DECODES CWMS Database " + dbUri + " as user " + username
				+ " with officeID=" + dbOfficeId);

				postConnectInit(appName, conn); // Make sure the versions and such are set
				setupKeyGenerator();

				if(cgl!=null)
				{
					cgl.setLoginSuccess(true);
				}

				try
				{
					hec.data.Units.getAvailableUnits();
				}
				catch (Exception ex)
				{
					Logger.instance().warning(module + " Exception in hec.data.Units.getAvailableUnits: " + ex);
				}

				try
				{
					baseParam.load(this);
				}
				catch (Exception ex)
				{
					String msg = "Cannot load baseParam Units Map: " + ex;
					failure(msg);
				}
				try(LoadingAppDAI la = this.makeLoadingAppDAO())
				{
					appId = la.lookupAppId(appName);
				}
				catch (DbIoException | NoSuchObjectException ex)
				{
					throw new BadConnectException("Unable to get loading app info",ex);
				}
			}
			catch(SQLException ex)
			{
				throw new BadConnectException("Pool was able to start but not retrieve connection",ex);
			}
		}
		else
		{
			throw new BadConnectException("unable to initialize pool for " + conInfo);
		}
		return appId;
	}

	@Override
	public void postConnectInit(String appName, Connection conn) throws BadConnectException
	{
		super.postConnectInit(appName, conn);

		try (DaoHelper dao = new DaoHelper(this, "init", conn))
		{
			dbOfficeCode = dao.getSingleResult("select office_code from cwms_20.av_office where office_id=?",
											   rs -> DbKey.createDbKey(rs, 1),
											   dbOfficeId);
		}
		catch (SQLException ex)
		{
			throw new BadConnectException("Unable to lookup DbOfficeCode from Office Id " + dbOfficeId, ex);
		}
	}

	public void setParmSDI(DbCompParm parm, DbKey siteId, String dtcode)
		throws DbIoException, NoSuchObjectException
	{
		debug3("setParmSDI siteId=" + siteId +
			", dtcode=" + dtcode);

		DataType dt = null;
		DataTypeDAI dataTypeDao = makeDataTypeDAO();
		try 
		{ 
			try { dataTypeDao.lookupDataType(dtcode); }
			catch(NoSuchObjectException ex)
			{
				// This combo of CWMS Param-SubParam doesn't exist yet in the
				// database as a 'datatype' object. Create it.
				dt = DataType.getDataType(Constants.datatype_CWMS, dtcode);
				dataTypeDao.writeDataType(dt);
			}

			String q = 
				"SELECT TS_CODE, LOCATION_ID FROM CWMS_V_TS_ID "
				+ "WHERE LOCATION_CODE = " + siteId
				+ " AND TS_ACTIVE_FLAG = 'T'"
				+ " AND upper(PARAMETER_ID) = "
						+ sqlString(dtcode.toUpperCase())
				+ " AND INTERVAL_ID = " + sqlString(parm.getInterval())
				+ " AND VERSION_ID = " + sqlString(parm.getVersion())
				+ " AND DURATION_ID = " + sqlString(parm.getDuration())
				+ " AND PARAMETER_TYPE_ID = " + sqlString(parm.getParamType());
			// Don't need to select on office id. It is implied by location code.

			ResultSet rs = dataTypeDao.doQuery(q);
			if (rs != null && rs.next())
			{
				DbKey sdi = DbKey.createDbKey(rs, 1);
				parm.setSiteDataTypeId(sdi);
				parm.addSiteName(new SiteName(null, Constants.snt_CWMS,
					rs.getString(2)));
			}
			else
				throw new NoSuchObjectException(
					"No Time Series with specified identifiers.");
			Site site = this.getSiteById(siteId);
			if (site != null)
				for(Iterator snit = site.getNames(); snit.hasNext(); )
					parm.addSiteName(snit.next());
		}
		catch(SQLException ex)
		{
			throw new DbIoException("setParmSDI: " + ex);
		}
		finally
		{
			dataTypeDao.close();
		}
	}

	@Override
	public TimeSeriesIdentifier expandSDI(DbCompParm parm)
		throws DbIoException, NoSuchObjectException
	{
		DbKey sdi = parm.getSiteDataTypeId();
		DbKey siteId = parm.getSiteId();
		DbKey datatypeId = parm.getDataTypeId();

		TimeSeriesDAI timeSeriesDAO = makeTimeSeriesDAO();
		TimeSeriesIdentifier tsid = null;
		try
		{
			if (!DbKey.isNull(sdi))
			{
				tsid = timeSeriesDAO.getTimeSeriesIdentifier(parm.getSiteDataTypeId());
				parm.setSite(tsid.getSite());
				parm.setDataType(tsid.getDataType());
			}
			else
			{
				if (!DbKey.isNull(siteId))
					parm.setSite(this.getSiteById(siteId));
				if (!DbKey.isNull(datatypeId))
					parm.setDataType(DataType.getDataType(datatypeId));
			}
		}
		finally
		{
			timeSeriesDAO.close();
		}

		return tsid;
	}

	/**
	 * CWMS TSDB stores ParamType.Duration.Version in the tab selector.
	 */
	public String getTableSelectorLabel()
	{
		return "Type.Dur.Version";
	}

	/**
	 * For CWMS we show all 5 path components for the site.
	 *
	 * {@inheritDoc}
	 */
	@Override
	public ArrayList getDataTypesForSite(DbKey siteId, DaiBase dao)
		throws DbIoException
	{
		String header[] = new String[5];
		header[0] = "Param";
		header[1] = "Param Type";
		header[2] = "Interval";
		header[3] = "Duration";
		header[4] = "Version";

		ArrayList ret = new ArrayList();
		ret.add(header);

		String q = "SELECT PARAMETER_ID, PARAMETER_TYPE_ID, INTERVAL_ID, "
			+ "DURATION_ID, VERSION_ID "
			+ "FROM CWMS_V_TS_ID "
			+ " where location_code = " + siteId
			+ " order by PARAMETER_ID, PARAMETER_TYPE_ID, INTERVAL_ID";
		try
		{
			ResultSet rs = dao.doQuery(q);
			while(rs.next())
			{
				String dtl[] = new String[5];
				for(int i=0; i<5; i++)
					dtl[i] = rs.getString(i+1);
				ret.add(dtl);
			}
		}
		catch(SQLException ex)
		{
			warning("Error reading Time series types for Location Code="
				+ siteId + ": " + ex);
		}
		return ret;
	}

	/**
	 * Validate the passed information to make sure it represents a valid
	 * parameter within this database. If not, throw ConstraintException.
	 */
	public void validateParm(DbKey siteId, String dtcode, String interval,
		String tabSel, int modelId)
		throws ConstraintException, DbIoException
	{
	}

	/** @return label to use for 'limit' column in tables. */
	public String getLimitLabel() { return "Qual Code"; }

	@Override
	public String flags2LimitCodes(int flags)
	{
		StringBuilder sb = new StringBuilder();
		if ((flags & CwmsFlags.SCREENED) != 0)
		{
			sb.append('S');
			if ((flags & CwmsFlags.VALIDITY_MISSING) != 0)
				sb.append('M');
			if ((flags & CwmsFlags.VALIDITY_REJECTED) != 0)
				sb.append('R');
			if ((flags & CwmsFlags.VALIDITY_QUESTIONABLE) != 0)
				sb.append('Q');
		}
		return sb.toString();
	}

	/** @return label to use for 'revision' column in tables. */
	public String getRevisionLabel() { return ""; }

	
	public boolean isCwms() { return true; }

	@Override
	public String[] getTsIdParts()
	{
		return CwmsTsId.tsIdParts;
	}

	@Override
	public TimeSeriesIdentifier transformTsidByCompParm(TimeSeriesDAI tsDAI,
			TimeSeriesIdentifier tsid, DbCompParm parm, boolean createTS,
			boolean fillInParm, String timeSeriesDisplayName)
		throws DbIoException, NoSuchObjectException, BadTimeSeriesException
	{
		if (tsid == null)
		{
			tsid = makeEmptyTsId();
		}

		String origString = tsid.getUniqueString();
		TimeSeriesIdentifier tsidRet = tsid.copyNoKey();
		boolean transformed = transformUniqueString(tsidRet, parm);

		if (transformed)
		{
			String uniqueString = tsidRet.getUniqueString();
			log.trace("CwmsTimeSeriesDb.transformTsid origString='{}', new string='{}', parm={}", origString, uniqueString, parm);
						
			FailableResult tmpTsId = tsDAI.findTimeSeriesIdentifier(uniqueString);

			if (tmpTsId.isFailure())
			{
				if (tmpTsId.getFailure() instanceof NoSuchObjectException)
				{
					if (createTS)
					{
						tsDAI.createTimeSeries(tsidRet);
						fillInParm = true;
					}
					else
					{
						log.trace("CwmsTimeSeriesDb.transformTsid no such time series '{}'", uniqueString);
						return null;
					}
				}
				else
				{
					ExceptionHelpers.throwDbIoNoSuchObject(tmpTsId.getFailure());
				}
			}
			else
			{
				log.trace("CwmsTimeSeriesDb.transformTsid time series '{}' exists OK.", uniqueString);
				tsidRet = tmpTsId.getSuccess();
			}
		}
		else
		{
			tsidRet = tsid;
		}

		if (timeSeriesDisplayName != null)
		{
			tsidRet.setDisplayName(timeSeriesDisplayName);
		}

		if (fillInParm)
		{
			parm.setSiteDataTypeId(tsidRet.getKey());
			parm.setInterval(tsidRet.getInterval());
			parm.setTableSelector(
				tsidRet.getPart("ParamType") + "."
				+ tsidRet.getPart("Duration") + "."
				+ tsidRet.getPart("Version"));
			parm.setDataType(tsidRet.getDataType());
			parm.setSite(tsidRet.getSite());
		}

		return tsidRet;
	}

	/**
	 * Overloaded from base class, transform the TSID unique string.
	 * @param tsidRet the time-series id to transform
	 * @param parm the templeat db comp parameter
	 * @return true if changes were made.
	 */
	public boolean transformUniqueString(TimeSeriesIdentifier tsidRet,
		DbCompParm parm)
	{
		boolean transformed = false;
		if (!(tsidRet instanceof CwmsTsId))
			return false;
		CwmsTsId ctsid = (CwmsTsId) tsidRet;

		SiteName sn = parm.getSiteName();
		if (sn != null)
		{
			tsidRet.setSiteName(sn.getNameValue());
			transformed = true;
			if (sn.site != null)
				tsidRet.setSite(sn.site);
			else
			{
				// Also lookup the site and set the ID and site object.
				SiteDAI siteDAO = makeSiteDAO();
				try
				{

					DbKey siteId = siteDAO.lookupSiteID(sn);
					tsidRet.setSite(siteDAO.getSiteById(siteId));
				}
				catch (Exception ex)
				{
					Logger.instance().warning("Cannot get site for sitename " + sn + ": " + ex);
				}
				finally
				{
					siteDAO.close();
				}
			}
		}
		else if (this.tsdbVersion >= TsdbDatabaseVersion.VERSION_14
			  && parm.getLocSpec() != null && parm.getLocSpec().length() > 0)
		{
			String morphed = morph(ctsid.getSiteName(), parm.getLocSpec());
			debug2("TSID site name '" + ctsid.getSiteName() + "' with loc spec '"
				+ parm.getLocSpec() + "' morphed to '" + morphed + "'");
			if (morphed == null)
				morphed = parm.getLocSpec();
			tsidRet.setSite(null);
			tsidRet.setSiteName("");
			SiteDAI siteDAO = makeSiteDAO();
			try
			{
				DbKey siteId = siteDAO.lookupSiteID(morphed);
				if (!DbKey.isNull(siteId))
				{
					tsidRet.setSite(siteDAO.getSiteById(siteId));
					tsidRet.setSiteName(morphed);
				}
			}
			catch (Exception ex)
			{
				Logger.instance().warning("Cannot get site for sitename " + morphed + ": " + ex);
			}
			finally
			{
				siteDAO.close();
			}
			transformed = true;
		}
		DataType dt = parm.getDataType();
		if (dt != null)
		{
			tsidRet.setDataType(dt);
			transformed = true;
		}
		else if (this.tsdbVersion >= TsdbDatabaseVersion.VERSION_14
			  && parm.getParamSpec() != null && parm.getParamSpec().length() > 0)
		{
			String morphed = morph(ctsid.getPart("param"), parm.getParamSpec());
			if (morphed == null)
				debug2("Unable to morph param '" + ctsid.getPart("param") + "' with param spec '"
					+ parm.getParamSpec() + "'");
			else
			{
				tsidRet.setDataType(null);
				tsidRet.setDataType(DataType.getDataType(Constants.datatype_CWMS, morphed));
				transformed = true;
			}
		}

		String s = parm.getParamType();
		if (s != null && s.trim().length() > 0)
		{
			tsidRet.setPart("ParamType", s);
			transformed = true;
		}
		s = parm.getInterval();
		if (s != null && s.trim().length() > 0)
		{
			tsidRet.setPart("Interval", s);
			transformed = true;
		}
		s = parm.getDuration();
		if (s != null && s.trim().length() > 0)
		{
			tsidRet.setPart("Duration", s);
			transformed = true;
		}
		s = parm.getVersion();
		if (s != null && s.trim().length() > 0)
		{
			if (s.contains("*"))
			{
				String morphed = morph(ctsid.getPart("version"), s);
				if (morphed == null)
					debug2("Unable to morph param '" + ctsid.getPart("version")
						+ "' with version spec '" + s + "'");
				else
				{
					ctsid.setVersion(morphed);
					transformed = true;
				}
			}
			else
				tsidRet.setPart("Version", s);
			transformed = true;
		}
		return transformed;
	}

	/**
	 * For version 6.3, morph the tsid part by the computation param part.
	 * @param tsidComponent The component from the TSID
	 * @param parmComponent The component in the comp parm, which may contain wildcards.
	 * @return the tsid component masked by the parm component, or null if can't match.
	 */
	public static String morph(String tsidComponent, String parmComponent)
	{
		// Examples:
		// tsid: A-B-C   parm: D-*-F   result: D-B-F
		// tsid: A-B     parm: *-E-F   result: A-E-F
		// tsid: A-B-C   parm: D-*     result: D-B-C
		// tsid: A       parm: D-*     result: null
		// tsid: A-B-C   parm: *-D     result: A-D
		// tsid: A-B     parm: *-      result: A

		// Check for a partial location specification (OpenDCS 6.3)
		String tps[] = tsidComponent.split("-");
		String pps[] = parmComponent.split("-");
		StringBuilder sb = new StringBuilder();
		for(int idx = 0; idx < pps.length; )
		{
			if (pps[idx].equals("*"))
			{
				if (idx >= tps.length)
					return null;
				else
				{
					// A trailing asterisk in the mask means copy in rest of tsid.
					// However a trailing hyphen means lop off the rest of tsid.
					if (idx == pps.length - 1
					 && !parmComponent.endsWith("-"))
					{
						for(int tidx = idx; tidx < tps.length; tidx++)
							sb.append(tps[tidx] + (tidx < tps.length-1 ? "-" : ""));
					}
					else
						sb.append(tps[idx]);
				}
			}
			else
				sb.append(pps[idx]);
			if (++idx < pps.length)
				sb.append("-");
		}
		return sb.toString();

		/*
		 * Note: the table_selector in cp_comp_ts_parm will be empty for a component that is
		 * completely undefined. The syntax is ParamType.Duration.Version[.SiteSpec.ParmSpec],
		 * So "Total.1Hour." means that Version is undefined and shows as  in the gui.
		 * This is different from "Total.1Hour.Something-*". Meaning that the first part of
		 * the subversion can be anything.
		 */
	}


	public String getDbOfficeId()
	{
		return dbOfficeId;
	}

	/**
	 * @return the parameter types as String[]
	 */
	@Override
	public String[] getParamTypes() throws DbIoException
	{
		String retStr[];

		try
		{
			ArrayList ret = listParamTypes();
			retStr = new String[ret.size()];
			for (int i=0; i listParamTypes()
		throws DbIoException
	{
		ArrayList ret = new ArrayList();
		String q = "select distinct parameter_type_id FROM CWMS_V_TS_ID"
			+ " WHERE upper(DB_OFFICE_ID) = " + sqlString(dbOfficeId.toUpperCase());

		DaiBase dao = new DaoBase(this, "CWMSDB");
		try
		{
			ResultSet rs = dao.doQuery(q);
			while (rs != null && rs.next())
				ret.add(rs.getString(1));
		}
		catch (SQLException ex)
		{
			throw new DbIoException("CwmsTimeSeriesDb.listParamTypes: " + ex);
		}
		finally
		{
			dao.close();
		}

		// MJM - these are the ones we know about for sure:
		if (!ret.contains("Inst"))
			ret.add("Inst");
		if (!ret.contains("Ave"))
			ret.add("Ave");
		if (!ret.contains("Max"))
			ret.add("Max");
		if (!ret.contains("Min"))
			ret.add("Min");
		if (!ret.contains("Total"))
			ret.add("Total");
		return ret;

	}

	@Override
	public String[] getValidPartChoices(String part)
	{
		if (part.equalsIgnoreCase("version"))
			return currentlyUsedVersions;
		return super.getValidPartChoices(part);
	}

	@Override
	public TimeSeriesIdentifier makeEmptyTsId()
	{
		return new CwmsTsId();
	}


	
	/**
	 * Use database-specific flag definitions to determine whether the
	 * passed variable should be considered 'questionable'.
	 * @param v the variable whose flags to check
	 * @return true if flags are questionable, false if okay.
	 */
	public boolean isQuestionable(NamedVariable v)
	{
		return (v.getFlags() & CwmsFlags.VALIDITY_MASK) == CwmsFlags.VALIDITY_QUESTIONABLE;
	}

	/**
	 * Use database-specific flag definitions to set the passed variable
	 * as 'questionable'.
	 * @param v the variable whose flags to set
	 */
	public void setQuestionable(Variable v)
	{
		v.setFlags( (v.getFlags() & (~CwmsFlags.VALIDITY_MASK)) | CwmsFlags.VALIDITY_QUESTIONABLE);
	}

	public boolean isOracle()
	{
		return true;
	}

	@Override
	public SiteDAI makeSiteDAO()
	{
		return new CwmsSiteDAO(this, dbOfficeId);
	}

	@Override
	public TimeSeriesDAI makeTimeSeriesDAO()
	{
		return new CwmsTimeSeriesDAO(this, dbOfficeId);
	}

	@Override
	public IntervalDAI makeIntervalDAO()
	{
		return new CwmsIntervalDAO(this, dbOfficeId);
	}

	@Override
	public ScheduleEntryDAI makeScheduleEntryDAO()
	{
		return null;
	}

	public void setDbUri(String dbUri)
	{
		this.dbUri = dbUri;
	}

	public DbKey getDbOfficeCode()
	{
		return this.dbOfficeCode;
	}

	public BaseParam getBaseParam()
	{
		return baseParam;
	}

	@Override
	public ScreeningDAI makeScreeningDAO()
		throws DbIoException
	{
		return new ScreeningDAO(this);
	}

	@Override
	public GroupHelper makeGroupHelper()
	{
		return new CwmsGroupHelper(this);
	}

	@Override
	public double rating(String specId, Date timeStamp, double... indeps)
		throws DbCompException, RangeException
	{
		// int nIndeps = indeps.length;
		// NOTE: indeps is already an array of doubles. I can pass
		// it directly to the rateOne function.
		
		String action = "reading rating";
		try (Connection tc = getConnection();
			CwmsRatingDao crd = new CwmsRatingDao(this))
		{
			crd.setManualConnection(tc);
			RatingSet ratingSet = crd.getRatingSet(specId);
			action = "rateOne";
			double d = ratingSet.rateOne(tc, timeStamp.getTime(), indeps);
			
			if (d == Const.UNDEFINED_DOUBLE)
			{
				StringBuilder sb = new StringBuilder();
				for(double x : indeps)
				{
					sb.append(x + ",");
				}
				sb.deleteCharAt(sb.length()-1);
				String msg = "Input values (" + sb.toString() + ") outside rating range.";
				warning(msg);
				throw new RangeException(msg);
			}
			return d;
		}
		catch (RatingException ex)
		{
			String msg = "Error while " + action + ", specId=" + specId;
			throw new RangeException(msg, ex);
		}
		catch (SQLException ex)
		{
			throw new DbCompException("Error during database operations.", ex);
		}
	}

	@Override
	public void closeConnection()
	{
		// Close prepared statements
		if (getMinStmt != null)
		{
			try { getMinStmt.close(); }
			catch(Exception ex) {}
			getMinStmt = null;
		}
		if (getTaskListStmt != null)
		{
			try { getTaskListStmt.close(); }
			catch(Exception ex) {}
			getTaskListStmt = null;
		}
	}

	@Override
	public ArrayList listVersions()
		throws DbIoException
	{
		ArrayList ret = new ArrayList();
		String q = "select distinct version_id from cwms_v_ts_id order by version_id";

		DaiBase dao = new DaoBase(this, "CWMS");
		try
		{
			ResultSet rs = dao.doQuery(q);
			while (rs != null && rs.next())
					ret.add(rs.getString(1));
		}
		catch (SQLException ex)
		{
			throw new DbIoException("CwmsTimeSeriesDb.listVersions: " + ex);
		}
		finally
		{
			dao.close();
		}

		return ret;
	}

	@Override
	public String getStorageUnitsForDataType(DataType dt)
	{
		String cwmsParam = null;
		if (dt.getStandard().equalsIgnoreCase(Constants.datatype_CWMS))
			cwmsParam = dt.getCode();
		else
		{
			DataType equiv = dt.findEquivalent(Constants.datatype_CWMS);
			if (equiv == null)
				return null;
			cwmsParam = equiv.getCode();
		}

		// Truncate to just base param
		int hyphen = cwmsParam.indexOf('-');
		if (hyphen > 0)
			cwmsParam = cwmsParam.substring(0, hyphen);
		return baseParam.getStoreUnits4Param(cwmsParam);
	}

	@Override
	public String flags2display(int flags)
	{
		return CwmsFlags.flags2Display(flags);
	}

	@Override
	public Connection getConnection()
	{
		// Called from DAOs to get a new connection from the pool.
		if (conInfo == null || conInfo.getLoginInfo() == null || pool == null)
		{
			failure("CwmsTimeSeriesDb.getConnection -- loginInfo is null! DB not initialized?");
			throw new RuntimeException("Invalid sequence of events. Attempt to retrieve DB connection before information initialized.");
		}
		try
		{
			return pool.getConnection();
		}
		catch(SQLException ex)
		{
			failure("Unable to get connection from pool: " + ex.getLocalizedMessage());
			throw new RuntimeException("Error retrieving connection.",ex);
		}
	}


	@Override
	public void freeConnection(Connection con)
	{
		try
		{
			pool.returnConnection(con);
		}
		catch(SQLException ex)
		{
			Logger.instance().warning("Unable to close returned connection: " + ex.getLocalizedMessage());
		}
	}
	
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy