DBA Data[Home] [Help]

PACKAGE BODY: APPS.MTH_SUSTAIN_ASPECT_PKG

Source


1 PACKAGE BODY MTH_SUSTAIN_ASPECT_PKG AS
2 /*$Header: mthesatb.pls 120.4 2010/12/07 00:22:18 gtippire noship $ */
3 
4 p_reading MeterReadingTable;
5 p_shift EntityShiftTable;
6 
7 
8 /* ****************************************************************************
9 * Function     		:IS_RAW_DATA_ROW_VALID                                *
10 * Description 	 	:Check if the raw from MTH_TAG_METER_READINGS_RAW is  *
11 *                  valid or not. If it violates any validation rules,         *
12 *                  return -1; otherwise, return 1                             *
13 * File Name             :MTHSUSAB.PLS                                         *
14 * Visibility            :Private                                              *
15 * Parameters            :p_tag_code - Tag code                                *
16 *                        p_reading_time - Reading time                        *
17 *                        p_tag_value -  tag value                             *
18 *                        p_is_number -  1 if tag value is number; 0 otherwise *
19 *                        p_is_cumulative -  1 to apply incremental logic;     *
20 *                                           0 otherwise                       *
21 *                        p_is_assending -  1 if tag is assending order;       *
22 *                                           0 otherwise                       *
23 *                        p_initial_value -  Tag initial value                 *
24 *                        p_max_reset_value -                                  *
25 *                        p_num_meters -  Number of meters associated with tag *
26 *                        p_prev_reading_time -  reading time for the previous *
27 *                                               tag reading                   *
28 *  OUT parameters:                                                            *
29 *                        p_is_valid_timestamp -  Whether the reading time in  *
30 *                         in the current entry can be used to calculate the   *
31 *                         from time of the enxt reading.                      *
32 * Return Value          : Found violations of the following rules:            *
33 *                         'NGV'  -	Usage value is negative.              *
34 *                         'OTR'  -	Usage value is out of range defined   *
35 *                                   for a cumulative tag.                     *
36 *                         'OTO'  - 	The raw reading data is out of order. *
37 *                         'DUP'  -	The raw reading data is duplicated.   *
38 *                         'MIM'  -  There is no meter associated with the tag *
39 *                         'IDS'  -  The data source is not for SUSTAINABILITY *
43 **************************************************************************** */
40 *                         'WFQ'  -  	Wrong frequency number                *
41 *                         'FTD'  -  	Data at the Future time               *
42 *                        NULL  - Valid row                                    *
44 
45 FUNCTION IS_RAW_DATA_ROW_VALID
46          (p_tag_code IN VARCHAR2,
47           p_reading_time IN DATE,
48           p_tag_value IN NUMBER,
49           p_is_number IN NUMBER,
50           p_is_cumulative IN NUMBER,
51           p_is_assending IN NUMBER,
52           p_initial_value IN NUMBER,
53           p_max_reset_value IN NUMBER,
54           p_tag_type IN varchar2,
55           p_frequency IN NUMBER,
56           p_num_meters IN NUMBER,
57           p_prev_reading_time IN DATE,
58           p_is_valid_timestamp OUT NOCOPY BOOLEAN) RETURN VARCHAR2
59 IS
60   v_is_valid NUMBER;
61   v_err_code VARCHAR2(255) := '';
62 BEGIN
63   p_is_valid_timestamp := FALSE;
64   -- Test the negative case first since other error cases
65   -- do not write information into the LATEST table.
66   IF (p_tag_value < 0) THEN
67     v_err_code :=  v_err_code || 'NGV ';
68     IF p_is_cumulative = 0 THEN
69       p_is_valid_timestamp := TRUE;
70     END IF;
71   END IF;
72   IF (p_tag_type IS NULL or p_tag_type <> 'SUSTAINABILITY') THEN
73     v_err_code :=  v_err_code || 'IDS ';
74   END IF;
75   IF (p_is_cumulative = 0 AND
76       p_frequency <= 0) THEN
77     v_err_code :=  v_err_code || 'WFQ ';
78     p_is_valid_timestamp := FALSE;
79   END IF;
80   IF (p_is_number = 1 AND p_is_cumulative = 1 AND
81       p_tag_value > p_max_reset_value) THEN
82     v_err_code :=  v_err_code || 'OTR ';
83     p_is_valid_timestamp := FALSE;
84   END IF;
85   IF (p_prev_reading_time IS NOT NULL AND
86       p_reading_time < p_prev_reading_time) THEN
87     v_err_code :=  v_err_code || 'OTO ';
88     p_is_valid_timestamp := FALSE;
89   END IF;
90   IF (p_prev_reading_time IS NOT NULL AND
91       p_reading_time = p_prev_reading_time) THEN
92     v_err_code :=  v_err_code || 'DUP ';
93     p_is_valid_timestamp := FALSE;
94   END IF;
95   IF (p_reading_time > SYSDATE) THEN
96     v_err_code :=  v_err_code || 'FTD ';
97     p_is_valid_timestamp := FALSE;
98   END IF;
99   IF (p_num_meters = 0) THEN
100     v_err_code :=  v_err_code || 'MIM ';
101     p_is_valid_timestamp := FALSE;
102   END IF;
103   IF (Length(v_err_code) = 0) THEN
104     v_err_code := NULL;
105     p_is_valid_timestamp := TRUE;
106   END IF;
107   RETURN v_err_code;
108 END IS_RAW_DATA_ROW_VALID;
109 
110 
111 
112 
113 /* ****************************************************************************
114 * Procedure    		:insert_row_to_err_tab                                *
115 * Description 	 	:Insert the error row into the error with error code  *
116 * File Name             :MTHSUSAB.PLS                                         *
117 * Visibility            :Private                                              *
118 * Parameters            :p_tag_code - Tag code                                *
119 *                        p_reading_time - Reading time                        *
120 *                        p_tag_value -  tag value                             *
121 *                        p_err_code -  Error codes                            *
122 * Return Value          :None                                                 *
123 **************************************************************************** */
124 PROCEDURE insert_row_to_err_tab(P_TAG_CODE IN VARCHAR2,
125                                 P_READING_TIME IN DATE,
126                                 P_TAG_VALUE IN NUMBER,
127                                 P_ERROR_CODE IN VARCHAR2)
128 IS
129   v_reprocess_ready_yn VARCHAR2(1) := 'N';
130   v_meter_reading_err_pk_key NUMBER;
131 BEGIN
132   INSERT INTO MTH_METER_READINGS_ERR
133       (METER_READINGS_ERR_PK_KEY, TO_TIME, USAGE_VALUE, TAG_CODE,
134        REPROCESS_READY_YN, ERR_CODE)
135     VALUES (MTH_METER_READINGS_ERR_S.NEXTVAL, P_READING_TIME,
136             P_TAG_VALUE, P_TAG_CODE, v_reprocess_ready_yn, P_ERROR_CODE);
137 
138   EXCEPTION
139     WHEN others THEN
140       NULL; -- Skip the error in this case
141 
142 END insert_row_to_err_tab;
143 
144 
145 
146 /* ****************************************************************************
147 * Function    		:get_incremental_value                                *
148 * Description 	 	:Get the incremental value for a tag value            *
149 * File Name             :MTHSUSAB.PLS                                         *
150 * Visibility            :Private                                              *
151 * Parameters            :p_tag_value -  tag value                             *
152 *                        p_is_number -  1 if tag value is number; 0 otherwise *
153 *                        p_is_cumulative -  1 to apply incremental logic;     *
154 *                                           0 otherwise                       *
155 *                        p_is_assending -  1 if tag is assending order;       *
156 *                                           0 otherwise                       *
157 *                        p_initial_value -  Tag initial value                 *
158 *                        p_max_reset_value -                                  *
159 *                        p_prev_tag_value -  Previous tag value               *
160 * Return Value          :Incremental value if incremental logic needs to be   *
161 *                          be applied; return p_tag_value otherwise           *
162 **************************************************************************** */
163 
164 FUNCTION get_incremental_value(P_TAG_VALUE IN NUMBER,
165                                P_IS_NUMBER IN NUMBER,
169                                P_MAX_RESET_VALUE IN NUMBER,
166                                P_IS_CUMULATIVE IN NUMBER,
167                                P_IS_ASSENDING IN NUMBER,
168                                P_INITIAL_VALUE IN NUMBER,
170                                p_prev_tag_value IN NUMBER)  RETURN NUMBER
171 IS
172   v_incr_value NUMBER;
173 BEGIN
174   -- 1. Do not need to apply the incremental logic
175   IF (P_IS_NUMBER IS NULL OR P_IS_NUMBER <> 1 OR
176       P_IS_CUMULATIVE IS NULL OR P_IS_CUMULATIVE <> 1) THEN
177     v_incr_value := P_TAG_VALUE;
178   ELSIF (P_IS_CUMULATIVE = 1 AND  P_IS_ASSENDING = 1 AND
179          p_prev_tag_value IS NOT NULL) THEN
180     -- 2. Assending tag and it is not the first reding
181     -- 2.1 not reset
182     v_incr_value := CASE WHEN P_TAG_VALUE >= p_prev_tag_value
183                          THEN P_TAG_VALUE - p_prev_tag_value
184                          -- 2.2 after reset
185                          ELSE P_MAX_RESET_VALUE - p_prev_tag_value +
186                               P_TAG_VALUE
187                     END;
188   ELSIF (P_IS_CUMULATIVE = 1 AND  P_IS_ASSENDING = 1 AND
189          p_prev_tag_value IS NULL) THEN
190     -- 3. Assending tag and it is the first reding
191     -- 3.1 First reading
192     v_incr_value := CASE WHEN P_TAG_VALUE >= P_INITIAL_VALUE
193                          THEN P_TAG_VALUE - P_INITIAL_VALUE
194                          -- 3.2 First reading but reset already
195                          ELSE P_MAX_RESET_VALUE - P_INITIAL_VALUE + P_TAG_VALUE
196                     END;
197   ELSIF (P_IS_CUMULATIVE = 1 AND  P_IS_ASSENDING = 0 AND
198          p_prev_tag_value IS NOT NULL) THEN
199     -- 4. Descending tag and it is not the first reding
200     -- 4.1 not reset
201     v_incr_value := CASE WHEN P_TAG_VALUE <= p_prev_tag_value
202                          THEN p_prev_tag_value - P_TAG_VALUE
203                          -- 2.2 after reset
204                          ELSE p_prev_tag_value + P_MAX_RESET_VALUE -
205                               P_TAG_VALUE
206                     END;
207   ELSIF (P_IS_CUMULATIVE = 1 AND  P_IS_ASSENDING = 0 AND
208          p_prev_tag_value IS NULL) THEN
209     -- 5. Descending tag and it is  the first reding
210     -- 4.1 not reset
211     v_incr_value := CASE WHEN P_TAG_VALUE <= P_INITIAL_VALUE
212                          THEN P_INITIAL_VALUE - P_TAG_VALUE
213                          -- 3.2 First reading but reset already
214                          ELSE P_INITIAL_VALUE + P_MAX_RESET_VALUE - P_TAG_VALUE
215                     END;
216   END IF;
217   RETURN v_incr_value;
218 
219 END get_incremental_value;
220 
221 
222 /* ****************************************************************************
223 * Procedure    		:insert_runtime_error                                 *
224 * Description 	 	:Insert runtime exception into mth_runtime_err table  *
225 * File Name             :MTHSUSAB.PLS                                         *
226 * Visibility            :Private                                              *
227 * Parameters            :p_proc_func_name -  Name of the calling procedure or *
228 *                                            function                         *
229 *                        p_error_code -      Oracle error code                *
230 *                        p_error_msg -       error message                    *
231 * Return Value          :None                                                 *
232 **************************************************************************** */
233 PROCEDURE insert_runtime_error(p_proc_func_name IN VARCHAR2,
234                                p_error_code IN NUMBER,
235                                p_error_msg IN VARCHAR2)
236 IS
237  v_module_name VARCHAR2(80);
238 BEGIN
239  v_module_name := 'MTH_SUSTAIN_ASPECT_PKG.' || p_proc_func_name;
240  INSERT INTO mth_runtime_err
241       ( MODULE, error_code, error_msg, timestamp) VALUES
242        (v_module_name, p_error_code, p_error_msg, SYSDATE);
243 
244 EXCEPTION
245    WHEN OTHERS THEN
246       ROLLBACK;
247 END insert_runtime_error;
248 
249 /* ****************************************************************************
250 * Procedure    		:insert_act_meters_to_readings                        *
251 * Description 	 	:Insert the actual meters associated with that tag    *
252 *                        into meter readings table                            *
253 * File Name             :MTHSUSAB.PLS                                         *
254 * Visibility            :Private                                              *
255 * Parameters            :p_tag_code -  tag code                               *
256 *                        p_reading_time -  tag reading time                   *
257 *                        p_incr_tag_value -  incremental tag reading          *
258 *                        p_prev_reading_time - reading time for previous one  *
259 *                        p_frequency - frequeycy ;  null if not incremental   *
260 *                        p_meter_keys_arr -  List of meter fk keys associated *
261 *                                            with the tag                     *
262 * Return Value          :None                                                 *
263 **************************************************************************** */
264 
265 PROCEDURE insert_act_meters_to_readings(p_tag_code IN VARCHAR2,
266                                     p_reading_time IN DATE,
267                                     p_incr_tag_value IN NUMBER,
268                                     p_prev_reading_time IN DATE,
269                                     p_frequency IN NUMBER,
270                                     p_meter_keys_arr IN DBMS_SQL.NUMBER_TABLE)
271 IS
272   v_from_time DATE;
273   v_processed_flag VARCHAR2(1) := 'N';
277 BEGIN
274   v_system_id NUMBER := -99999;
275   v_from_time_using_frequency DATE;
276 
278   v_from_time_using_frequency := p_reading_time - p_frequency + 1/86400;
279   v_from_time := CASE  WHEN p_prev_reading_time IS NULL THEN p_reading_time
280                        ELSE p_prev_reading_time + 1/86400
281                  END;
282 
283   -- Use freqnecy when available if it is the first reading or
284   -- the reading is delayed
285   IF v_from_time_using_frequency IS NOT NULL AND
286        p_prev_reading_time IS NOT NULL AND
287        v_from_time_using_frequency > v_from_time OR
288        p_prev_reading_time IS NULL AND
289        v_from_time_using_frequency IS NOT NULL THEN
290     v_from_time := v_from_time_using_frequency;
291   END IF;
292 
293   IF (p_meter_keys_arr IS NOT NULL) THEN
294     FORALL i IN 1..p_meter_keys_arr.Count
295       INSERT INTO MTH_METER_READINGS
296             (METER_FK_KEY, FROM_TIME, TO_TIME, USAGE_VALUE, PROCESSED_FLAG,
297               CREATION_DATE, LAST_UPDATE_DATE, CREATION_SYSTEM_ID,
298               LAST_UPDATE_SYSTEM_ID) VALUES
299             (p_meter_keys_arr(i), v_from_time, p_reading_time,
300               p_incr_tag_value, v_processed_flag, SYSDATE, SYSDATE,
301             v_system_id, v_system_id);
302   END IF;
303 
304 END insert_act_meters_to_readings;
305 
306 
307 
308 /* ****************************************************************************
309 * Procedure    		:upsert_tag_to_latest_tab                             *
310 * Description 	 	:Update the latest reading time and tag value         *
311 *                  for a tag if table MTH_TAG_METER_READINGS_LATEST already   *
312 *                  has a entry for the tag. Otherwise, insert a new row       *
313 * File Name             :MTHSUSAB.PLS                                         *
314 * Visibility            :Private                                              *
315 * Parameters            :p_tag_code -  tag code                               *
316 *                        p_latest_reading_time - reading time of the latest   *
317 *                        p_latest_tag_value -  latest tag reading             *
318 *                        p_lookup_entry_exist - whether the entry with the    *
319 *                            same tag code exists in the                      *
320 *                            MTH_TAG_METER_READINGS_LATEST or not             *
321 * Return Value          :None                                                 *
322 **************************************************************************** */
323 
324 PROCEDURE upsert_tag_to_latest_tab(p_tag_code IN VARCHAR2,
325                                    p_latest_reading_time IN DATE,
326                                    p_latest_tag_value IN NUMBER,
327                                    p_lookup_entry_exist IN BOOLEAN)
328 IS
329 BEGIN
330   -- If the entry exists, do the update; otherwise, do the insert
331   IF (p_lookup_entry_exist) THEN
332     UPDATE MTH_TAG_METER_READINGS_LATEST
333     SET    reading_time = p_latest_reading_time, tag_value = p_latest_tag_value
334     WHERE  tag_code = p_tag_code;
335   ELSE
336     INSERT INTO MTH_TAG_METER_READINGS_LATEST
337            (TAG_CODE, READING_TIME, TAG_VALUE) VALUES
338            (p_tag_code, p_latest_reading_time, p_latest_tag_value);
339   END IF;
340 
341 END upsert_tag_to_latest_tab;
342 
343 
344 /* ****************************************************************************
345 * Procedure		:LOAD_ACT_METER_RAW_TO_READINGS                       *
346 * Description 	 	:Load data from tag meter raw for energy consumption  *
347 * in MTH_TAG_METER_READINGS_RAW into meter readings table MTH_METER_READINGS  *
348 * for actual meters                                                           *
349 **************************************************************************** */
350 
351 PROCEDURE LOAD_ACT_METER_RAW_TO_READINGS(p_curr_partition IN NUMBER)
352 IS
353 --  TYPE DBMS_SQL.NUMBER_TABLE IS TABLE OF NUMBER;
354 
355 
356   -- Fetch raw data for active tags from the patition
357   -- ordered by TAG_CODE, READING_TIME
358   CURSOR c_getRawData (p_processing_flag IN NUMBER) IS
359     SELECT R.TAG_CODE, R.READING_TIME,  R.TAG_VALUE,
360            Decode(DATA_TYPE, 'NUM', 1, 0) IS_NUMBER,
361            CASE WHEN Nvl(S.APPLY_INCREMENTAL_LOGIC, 'N') = 'Y' AND
362                      T.READING_TYPE = 'CHNG'
363                 THEN 1
364                 ELSE 0
365            END AS IS_CUMULATIVE,
366            Decode(T.ORDER_TYPE, 'ASC', 1, 0) IS_ASSENDING,
367            T.INITIAL_VALUE, T.MAX_RESET_VALUE, S.TAG_TYPE,
368            T.FREQUENCY_IN_MINUTES  / 1440 as FREQUENCY
369     FROM MTH_TAG_METER_READINGS_RAW R, MTH_TAG_MASTER T,
370          MTH_TAG_DATA_SOURCES S
371     WHERE
372           R.TAG_CODE = T.TAG_CODE (+) AND
373           'ACTIVE' = T.STATUS (+)  AND
374           T.TAG_DATA_SOURCE_FK_KEY = S.TAG_DATA_SOURCE_PK_KEY (+) AND
375           R.PROCESSING_FLAG =  p_processing_flag
376     ORDER BY TAG_CODE, READING_TIME;
377 
378 
379   -- Fetch actual meter keys for the given tag code
380   CURSOR c_getMetersForTag (p_tag_code IN VARCHAR2) IS
381     SELECT METER_PK_KEY
382     FROM MTH_METERS
383     WHERE TAG_CODE = p_tag_code AND meter_type = 'ACT';
384 
385   -- Fetch the previous reading time for the given tag code
386   CURSOR c_getPrevReadingTimeForTag (p_tag_code IN VARCHAR2) IS
387     SELECT TAG_VALUE, READING_TIME
388     FROM MTH_TAG_METER_READINGS_LATEST
389     WHERE TAG_CODE = p_tag_code;
390 
391 
392   v_raw_tab_name VARCHAR2(30) := 'MTH_TAG_METER_READINGS_RAW';
393   v_curr_partition NUMBER := p_curr_partition;
397   v_err_code VARCHAR2(255);
394   v_prev_tag_code VARCHAR2(255) := NULL;
395   v_curr_tag_code VARCHAR2(255) := NULL;
396   v_meter_fk_key_array  DBMS_SQL.NUMBER_TABLE;
398   v_lookup_entry_exist boolean;
399   v_prev_reading_time DATE := NULL;
400   v_prev_tag_value NUMBER := NULL;
401   v_incr_tag_value NUMBER;
402   -- Record the number of insert and update operations
403   v_num_insert_update NUMBER := 0;
404   v_reprocess_ready_yn VARCHAR2(1) := 'N';
405   v_meter_reading_err_pk_key NUMBER;
406   v_error_code NUMBER := NULL;
407   v_error_msg VARCHAR2(4000);
408   v_is_valid_timestamp BOOLEAN := FALSE;
409 
410   -- Keep the information of the last reading to be used to update LATEST table
411   -- after the LOOP for the latest valid timestamp of the last tag code
412   v_last_tag_code VARCHAR2(255) := NULL;
413   v_last_reading_time DATE := NULL;
414   v_last_tag_value NUMBER := NULL;
415 
416 BEGIN
417   -- 1. First switch the partition for the meter readings raw table
418   --mth_util_pkg.switch_column_default_value(v_raw_tab_name, v_curr_partition);
419   IF (v_curr_partition = 0) THEN
420     -- No data available in the table to be processed
421     RETURN;
422   END IF;
423 
424   -- 2. Fetch the raw data for active tag and process each row
425   FOR r_raw_data IN c_getRawData(v_curr_partition) LOOP
426     v_curr_tag_code := r_raw_data.TAG_CODE;
427 
428     -- 2.0 Update/Create entry in MTH_TAG_METER_READINGS_LATEST for previous tag
429     --     that is stored in v_last_... .
430     --     only if the there is no error or the timestamp is valid even though
431     --     there is an error in the preivoud set of values kept in v_last....
432     IF (v_last_tag_code IS NOT NULL AND v_last_tag_code <> v_curr_tag_code) THEN
433       upsert_tag_to_latest_tab(v_last_tag_code,
434                                v_last_reading_time,
435                                v_last_tag_value,
436                                v_lookup_entry_exist);
437       v_num_insert_update := v_num_insert_update + 1;
438 
439       -- Erase the information for the last record since it has been saved
440       -- into the LATEST table already.
441       v_last_tag_code := NULL;
442       v_last_reading_time := NULL;
443       v_last_tag_value := NULL;
444     END IF;
445 
446     -- 2.1 Find the meters and latest reading for the new tag code
447     IF (v_prev_tag_code IS NULL OR v_prev_tag_code <> v_curr_tag_code) THEN
448       -- 2.1.0 Reset the previous reading for the new tag code
449       v_prev_tag_value := NULL;
450       v_prev_reading_time := NULL;
451 
452       -- 2.1.1 Fetch meter keys for the specified tag code
453       OPEN c_getMetersForTag(v_curr_tag_code);
454       FETCH c_getMetersForTag BULK COLLECT INTO v_meter_fk_key_array;
455       CLOSE c_getMetersForTag;
456 
457       -- 2.1.2 Find previous reading time and tag value for the  currenttag
458       --       in the lookup table MTH_TAG_METER_READINGS_LATEST
459       OPEN c_getPrevReadingTimeForTag(v_curr_tag_code);
460       FETCH c_getPrevReadingTimeForTag INTO
461               v_prev_tag_value, v_prev_reading_time;
462       CLOSE c_getPrevReadingTimeForTag;
463       v_lookup_entry_exist :=  v_prev_reading_time IS NOT NULL;
464     END IF;
465 
466 
467 
468     -- 2.2 Validate the raw data
469     v_err_code := IS_RAW_DATA_ROW_VALID (r_raw_data.TAG_CODE,
470                                          r_raw_data.READING_TIME,
471                                          r_raw_data.TAG_VALUE,
472                                          r_raw_data.IS_NUMBER,
473                                          r_raw_data.IS_CUMULATIVE,
474                                          r_raw_data.IS_ASSENDING,
475                                          r_raw_data.INITIAL_VALUE,
476                                          r_raw_data.MAX_RESET_VALUE,
477                                          r_raw_data.TAG_TYPE,
478                                          r_raw_data.FREQUENCY,
479                                          v_meter_fk_key_array.Count,
480                                          v_prev_reading_time,
481                                          v_is_valid_timestamp);
482 
483     -- 2.3 Insert data into either meter readings or error table
484     IF (v_err_code IS NOT NULL OR Length(v_err_code) > 0) THEN
485       -- 2.3.1 Insert the error row to error table  if there is any error
486       INSERT INTO MTH_METER_READINGS_ERR
487           (METER_READINGS_ERR_PK_KEY, TO_TIME, USAGE_VALUE, TAG_CODE,
488           REPROCESS_READY_YN, ERR_CODE)
489         VALUES (MTH_METER_READINGS_ERR_S.NEXTVAL, r_raw_data.READING_TIME,
490                 r_raw_data.TAG_VALUE, r_raw_data.TAG_CODE,
491                 v_reprocess_ready_yn, v_err_code);
492       v_num_insert_update := v_num_insert_update + 1;
493     ELSE
494       -- 2.3.2 Get the incremental value
495       v_incr_tag_value := get_incremental_value(r_raw_data.TAG_VALUE,
496                                                 r_raw_data.IS_NUMBER,
497                                                 r_raw_data.IS_CUMULATIVE,
498                                                 r_raw_data.IS_ASSENDING,
499                                                 r_raw_data.INITIAL_VALUE,
500                                                 r_raw_data.MAX_RESET_VALUE,
501                                                 v_prev_tag_value);
502 
503       -- 2.3.3 Insert the data into the meter readings table
504       insert_act_meters_to_readings(r_raw_data.TAG_CODE,
505                                     r_raw_data.READING_TIME,
506                                     v_incr_tag_value,
507                                     v_prev_reading_time,
508                                     CASE r_raw_data.IS_CUMULATIVE
509                                          WHEN 0 THEN r_raw_data.FREQUENCY
510                                          ELSE NULL END,
514     END IF;
511                                     v_meter_fk_key_array);
512 
513       v_num_insert_update := v_num_insert_update + v_meter_fk_key_array.Count;
515 
516     -- 2.4 Save the current data as previous data, which can be used for :
517     --     - Use the previous reading time - 1 second as FROM TIME for new data
518     --     - Use the previous tag value to calcuate the incremental value
519     --     - Save all of above into lookup table  MTH_TAG_METER_READINGS_LATEST
520     --       when processing the last reading for this tag, wihch can be
521     --       identified by find the next and different tag code
522   IF (v_err_code IS NULL OR Length(v_err_code) = 0) or
523        Length(v_err_code) > 0 and v_is_valid_timestamp THEN
524       v_prev_tag_code :=  v_curr_tag_code;
525       v_prev_tag_value := r_raw_data.TAG_VALUE;
526       v_prev_reading_time := Greatest(r_raw_data.READING_TIME,
527                                       Nvl(v_prev_reading_time,
528                                           r_raw_data.READING_TIME));
529 
530       -- Update the information for the last reading to update LATEST table
531       -- for step 2.6
532       v_last_tag_code := v_prev_tag_code;
533       v_last_reading_time := v_prev_reading_time;
534       v_last_tag_value := v_prev_tag_value;
535     END IF;
536 
537   END LOOP;
538 
539   -- 2.6 Update/Create entry in MTH_TAG_METER_READINGS_LATEST for the last tag.
540   --     Since we have kept the information for the last reading, just
541   --     need to make sure that v_last_tag_code exists.
542   IF (v_last_tag_code is not NULL) THEN
543     upsert_tag_to_latest_tab(v_last_tag_code,
544                              v_last_reading_time,
545                              v_last_tag_value,
546                              v_lookup_entry_exist);
547   END IF;
548 
549 EXCEPTION
550    WHEN OTHERS THEN
551       ROLLBACK;
552       v_error_code := SQLCODE;
553       v_error_msg :=  SQLERRM;
554       insert_runtime_error('insert_act_meters_to_readings',
555                            v_error_code,
556                            v_error_msg);
557       COMMIT;
558       RAISE;
559 
560 END LOAD_ACT_METER_RAW_TO_READINGS;
561 
562 
563 
564 
565 /* ****************************************************************************
566 * Function	:getEquipPowerRating                                          *
567 * Description 	:Get the equipment power rating. If it is not an equipment,   *
568 *                  return 0.                                                  *
569 * File Name             :MTHSUSAB.PLS                                         *
570 * Visibility            :Private                                              *
571 * Parameters            :p_entity_fk_key - type of the entity                 *
572 *                        p_entity_type - the ending point of time to          *
573 * Return Value          :Power rating of the equipment. If it is not an       *
574                          equipment, return 0.                                 *
575 **************************************************************************** */
576 
577 FUNCTION getEquipPowerRating(p_entity_fk_key IN NUMBER,
578                              p_entity_type IN VARCHAR2) RETURN NUMBER
579 IS
580   v_power_rating NUMBER := 0;
581 BEGIN
582   IF (p_entity_type = 'EQUIPMENT') THEN
583     SELECT power_rating INTO v_power_rating
584     FROM   mth_equipments_d
585     WHERE  equipment_pk_key = p_entity_fk_key;
586   END IF;
587   IF (v_power_rating IS NULL) THEN
588     v_power_rating := 0;
589   END IF;
590   RETURN v_power_rating;
591 
592   EXCEPTION --generic exception to capture all the errors
593   -- TO DO: Need to report the error
594     WHEN others THEN
595       RETURN 0;
596 END getEquipPowerRating;
597 
598 
599 /* ****************************************************************************
600 * Function	:getSFTInOneHour                                              *
601 * Description  	:Get shift availability time in the specified hour for the    *
602 *                  equipment power rating. If it is not an equipment, return 0*
603 * File Name             :MTHSUSAB.PLS                                         *
604 * Visibility            :Private                                              *
605 * Parameters            :p_from_time - the starting point of time to          *
606 *                                      calculate the virtual meter.           *
607 *                        p_to_time - The end time                             *
608 *                        p_cal_avb_time - true for shift available; false otw *
609 *                        p_shifts - nested table contains the shifts          *
610 *                        p_shifts_idx - current index to the shifts table     *
611 * Return Value          :Time in hour for the for the shift availability or   *
612 *                        not availability time within the hour specified      *
613 **************************************************************************** */
614 
615 FUNCTION getSFTInOneHour(p_from_time IN DATE,
616                          p_to_time IN DATE,
617                          p_cal_avb_time IN BOOLEAN,
618                          p_shifts IN shift_array_type,
619                          p_shifts_idx IN OUT NOCOPY NUMBER)
620            RETURN NUMBER
621 IS
622   v_hours NUMBER := 0;
623   v_continue BOOLEAN := TRUE;
624   v_from_time DATE := p_from_time;
625 BEGIN
626   IF p_cal_avb_time IS NULL THEN
627   -- Wrong type
628     RETURN 0;
629   END IF;
630 
631   WHILE (p_shifts_idx <= p_shifts.Count AND v_from_time < p_to_time AND
632          v_continue) LOOP
633          --Dbms_Output.put_line(' p_shifts_idx : ' || p_shifts_idx);
634          --Dbms_Output.put_line(' p_shifts.Count  : ' || p_shifts.Count );
638       -- from time, to time:     |----------------|
635     IF (p_to_time < p_shifts(p_shifts_idx).from_date) THEN
636       -- No more shift for the tiem range  since the current shift
637       -- is beyond the time range
639       -- shift start and end:                         |---------------------|
640       v_continue := FALSE;
641     ELSIF  (v_from_time > p_shifts(p_shifts_idx).to_date) THEN
642       -- The current time range is ahead of the shift, move the shift forward
643       -- from time, to time:                               |----------------|
644       -- shift start and end:    |---------------------|
645       p_shifts_idx := p_shifts_idx + 1;
646       -- there is intersection between the shift and the time range
647     ELSIF (p_to_time >= p_shifts(p_shifts_idx).from_date AND
648            p_to_time <= p_shifts(p_shifts_idx).To_Date) THEN
649       -- to_time is within the range of the shift
650       -- from time, to time:       |----------------|
651       -- shift start and end:          |----------------------------|
652       -- or
653       -- from time, to time:             |----------------|
654       -- shift start and end:          |----------------------------|
655       -- time range overlaps with shift, but does not include the end of shift
656       IF (p_cal_avb_time AND p_shifts(p_shifts_idx).availability_flag = 'Y' OR
657           NOT p_cal_avb_time AND p_shifts(p_shifts_idx).availability_flag = 'N')
658       THEN
659         v_hours := v_hours + (Least(p_shifts(p_shifts_idx).To_Date, p_to_time)
660                      - Greatest(p_shifts(p_shifts_idx).from_date, v_from_time))
661                      * 24 + 1/3600;
662       END IF;
663       v_from_time := Least(p_shifts(p_shifts_idx).To_Date, p_to_time) +
664                      1/86400;
665       v_continue := FALSE;
666     ELSIF (p_from_time <= p_shifts(p_shifts_idx).from_date AND
667            p_to_time >= p_shifts(p_shifts_idx).To_Date) THEN
668       -- The shift is within the time range
669       -- from time, to time:         |--------------------------------|
670       -- shift start and end:             |---------------------|
671       IF (p_cal_avb_time AND p_shifts(p_shifts_idx).availability_flag = 'Y' OR
672          NOT p_cal_avb_time AND p_shifts(p_shifts_idx).availability_flag = 'N')
673       THEN
674         v_hours := v_hours + (Least(p_shifts(p_shifts_idx).To_Date, p_to_time)
675                      - Greatest(p_shifts(p_shifts_idx).from_date, v_from_time))
676                      * 24 + 1/3600;
677       END IF;
678       v_from_time := Least(p_shifts(p_shifts_idx).To_Date, p_to_time) +
679                      1/86400;
680       v_continue := TRUE;
681       p_shifts_idx := p_shifts_idx + 1;
682     ELSIF (p_from_time >= p_shifts(p_shifts_idx).from_date AND
683            p_from_time <= p_shifts(p_shifts_idx).To_Date) THEN
684       -- from time is within the range of the shift, but not the to time.
685       -- from time, to time:                         |----------------|
686       -- shift start and end:          |---------------------|
687       -- time range overlaps with shift, but does not include the end of shift
688       IF (p_cal_avb_time AND p_shifts(p_shifts_idx).availability_flag = 'Y' OR
689          NOT p_cal_avb_time AND p_shifts(p_shifts_idx).availability_flag = 'N')
690       THEN
691         v_hours := v_hours + (Least(p_shifts(p_shifts_idx).To_Date, p_to_time)
692                      - Greatest(p_shifts(p_shifts_idx).from_date, v_from_time))
693                      * 24 + 1/3600;
694       END IF;
695       v_from_time := Least(p_shifts(p_shifts_idx).To_Date, p_to_time) +
696                      1/86400;
697       v_continue := TRUE;
698       p_shifts_idx := p_shifts_idx + 1;
699     END IF;
700   END LOOP;
701   RETURN v_hours;
702 
703   EXCEPTION
704     WHEN OTHERS THEN
705       RETURN 0;
706 END getSFTInOneHour;
707 
708 /* ****************************************************************************
709 * Function	:getCompValuesForSFT                                          *
710 * Description  	:Get shift availability time in hours for an entity in each   *
711 *                  hour during the given time range.                          *
712 * File Name             :MTHSUSAB.PLS                                         *
713 * Visibility            :Private                                              *
714 * Parameters            :p_entity_fk_key - type of the entity                 *
715 *                        p_entity_type - the ending point of time to          *
716 *                        p_component_value - Component value for shift avail: *
717 *                                                1) Available AVB             *
718 *                                                2) Non-Available NAVB        *
719 *                        p_from_time - the starting point of time to          *
720 *                                      calculate the virtual meter.           *
721 *                        p_to_time - the ending point of time to              *
722 *                                      calculate the virtual meter            *
723 * Return Value          :Collection of shift availability time for each       *
724 *                        hour during the given time range.                    *
725 **************************************************************************** */
726 
727 FUNCTION getCompValuesForSFT(p_entity_fk_key IN NUMBER,
728                              p_entity_type IN VARCHAR2,
729                              p_component_value IN VARCHAR2,
730                              p_from_time IN DATE,
731                              p_to_time IN DATE) RETURN DBMS_SQL.NUMBER_TABLE
732 IS
733   CURSOR c_getShifts(p_entity_type IN VARCHAR2, p_entity_fk_key IN VARCHAR2,
734                      p_from_time IN DATE, p_to_time IN DATE) IS
735     SELECT from_date, To_Date, availability_flag
736     FROM   MTH_EQUIPMENT_SHIFTS_D
737     WHERE  equipment_fk_key = p_entity_fk_key AND
738            UPPER(entity_type) = p_entity_type  AND
742 
739            To_Date >= p_from_time AND
740            from_date <= p_to_time
741     ORDER BY from_date;
743   v_shifts shift_array_type;
744   v_comp_values DBMS_SQL.NUMBER_TABLE;
745 
746   v_num_elements NUMBER;
747   v_from_time DATE := p_from_time;
748   v_to_time DATE;
749   v_end_from_time DATE := p_to_time - 1/24;
750   v_one_hour NUMBER := 1/24;
751   i NUMBER := 1;
752   v_value NUMBER;
753   -- Record the current index for the shifts nested table v_shifts
754   v_shifts_idx NUMBER := 1;
755   v_for_shift_available BOOLEAN;
756 
757 
758 BEGIN
759   OPEN c_getShifts(p_entity_type, p_entity_fk_key,p_from_time, p_to_time);
760   FETCH c_getShifts BULK COLLECT INTO v_shifts;
761   CLOSE c_getShifts;
762 
763   v_num_elements :=  Trunc((p_to_time - p_from_time) * 24) + 1;
764     IF (p_component_value = 'AVB') THEN
765       v_for_shift_available := TRUE;
766     ELSIF (p_component_value = 'NAVB') THEN
767       v_for_shift_available := FALSE;
768     ELSE
769       v_for_shift_available := NULL;
770     END IF;
771     WHILE (v_from_time <= v_end_from_time) LOOP
772       v_to_time :=  v_from_time + 1/24 - 1/86400;
773       v_comp_values(i) := getSFTInOneHour(v_from_time,
774                                           v_to_time,
775                                           v_for_shift_available,
776                                           v_shifts,
777                                           v_shifts_idx);
778       i := i + 1;
779       v_from_time := v_from_time + v_one_hour;
780     END LOOP;
781 
782   RETURN v_comp_values;
783 END getCompValuesForSFT;
784 
785 
786 /* ****************************************************************************
787 * Function	:getstatusTimeInOneHour                                       *
788 * Description  	:Get shift availability time in the specified hour for the    *
789 *                  equipment power rating. If it is not an equipment, return 0*
790 * File Name             :MTHSUSAB.PLS                                         *
791 * Visibility            :Private                                              *
792 * Parameters            :p_from_time - the starting point of time to          *
793 *                                      calculate the virtual meter.           *
794 *                        p_to_time - The end time                             *
795 *                        p_component_value - The possible values are          *
796 *                                                1) Run Time RT               *
797 *                                                2) Down Time DT              *
798 *                                                3) Idle Time IT              *
799 *                                                4) Off Time  OT              *
800 *                        p_status - nested table contains the status times    *
801 *                        p_idx - current index to the status table            *
802 * Return Value          :Time in hour for the for the specified status        *
803 *                         within the hour specified;                          *
804                          NULL if the status is not available for the          *
805 *                        specified time range. Need to wait for the status    *
806 *                        to be available.                                     *
807 **************************************************************************** */
808 
809 FUNCTION getstatusTimeInOneHour(p_from_time IN DATE,
810                                 p_to_time IN DATE,
811                                 p_component_value IN varchar2,
812                                 p_status IN status_array_type,
813                                 p_idx IN OUT NOCOPY NUMBER)
814            RETURN NUMBER
815 IS
816   v_hours NUMBER := 0;
817   v_time_range NUMBER := 0;
818   v_continue BOOLEAN := TRUE;
819   v_from_time DATE := p_from_time;
820   v_time_for_status NUMBER;
821 BEGIN
822   WHILE (p_idx <= p_status.Count AND v_from_time < p_to_time AND
823          v_continue) LOOP
824 
825     IF (p_to_time >= p_status(p_idx).from_date AND
826         p_status(p_idx).To_Date IS NULL) THEN
827      -- The status range is still open without ending time. Need to wait until
828      -- the status range is closed in order to calculate the hours for the
829      -- status
830       -- from time, to time:     |----------------|
831       -- status start and end:           |-------------------...(end time=NULL)
832       v_hours := NULL;
833       v_continue := FALSE;
834     ELSIF (p_to_time < p_status(p_idx).from_date) THEN
835       -- No more status for the time range  since the current status range
836       -- is beyond the time range
837       -- from time, to time:     |----------------|
838       -- status start and end:                         |---------------------|
839 
840       v_continue := FALSE;
841     ELSIF  (v_from_time > p_status(p_idx).to_date) THEN
842       -- The current time range is ahead of the status range, move the status
843       -- range forward
844       -- from time, to time:                               |----------------|
845       -- status start and end:    |---------------------|
846 
847       p_idx := p_idx + 1;
848       -- there is intersection between the status range and the time range
849     ELSIF (p_to_time >= p_status(p_idx).from_date AND
850            p_to_time <= p_status(p_idx).To_Date) THEN
851       -- to_time is within the range of the status range
852       -- from time, to time:       |----------------|
853       -- status start and end:          |----------------------------|
854       -- or
855       -- from time, to time:              |----------------|
856       -- status start and end:          |----------------------------|
857       -- time range overlaps with status, but does not include end of status
861 
858       v_time_range := (Least(p_status(p_idx).To_Date, p_to_time) -
859                    Greatest(p_status(p_idx).from_date, v_from_time)) + 1/86400;
860       --Dbms_Output.put_line(' case 2 v_hours before : ' || v_hours);
862       v_hours := v_hours + v_time_range / (p_status(p_idx).to_date -
863                                       p_status(p_idx).from_date +
864                                       1/86400) *
865                            CASE p_component_value
866                             WHEN 'RT' THEN Nvl(p_status(p_idx).run_hours, 0)
867                             WHEN 'DT' THEN Nvl(p_status(p_idx).down_hours, 0)
868                             WHEN 'IT' THEN Nvl(p_status(p_idx).idle_hours, 0)
869                             WHEN 'OT' THEN Nvl(p_status(p_idx).off_hours, 0)
870                             ELSE 0
871                             END;
872       --v_from_time := Least(p_status(p_idx).To_Date, p_to_time) + 1/86400;
873       v_continue := FALSE;
874       --Dbms_Output.put_line(' case 2 v_hours : ' || v_hours);
875 
876     ELSIF (p_from_time <= p_status(p_idx).from_date AND
877            p_to_time >= p_status(p_idx).To_Date) THEN
878       -- The status record is within the time range
879       -- from time, to time:         |--------------------------------|
880       -- status start and end:            |------------------|
881       --v_time_range := (Least(p_status(p_idx).To_Date, p_to_time) -
882       --             Greatest(p_status(p_idx).from_date, v_from_time)) + 1/86400;
883 
884      -- v_hours := v_hours + v_time_range / (p_status(p_idx).to_date -
885       --                                p_status(p_idx).from_date +
886       --                                1/86400) *
887       v_hours := v_hours +  CASE p_component_value
888                             WHEN 'RT' THEN Nvl(p_status(p_idx).run_hours, 0)
889                             WHEN 'DT' THEN Nvl(p_status(p_idx).down_hours, 0)
890                             WHEN 'IT' THEN Nvl(p_status(p_idx).idle_hours, 0)
891                             WHEN 'OT' THEN Nvl(p_status(p_idx).off_hours, 0)
892                             ELSE 0
893                             END;
894      -- v_from_time := Least(p_status(p_idx).To_Date, p_to_time) + 1/86400;
895       v_continue := TRUE;
896       p_idx := p_idx + 1;
897             --Dbms_Output.put_line(' case 3 v_hours : ' || v_hours);
898 
899     ELSIF (p_from_time >= p_status(p_idx).from_date AND
900            p_from_time <= p_status(p_idx).To_Date) THEN
901       -- from time is within the range of the status range, but not the to time.
902       -- from time, to time:                         |----------------|
903       -- status start and end:          |---------------------|
904       -- time range overlaps with status, but does not include the end of status
905       v_time_range := (Least(p_status(p_idx).To_Date, p_to_time) -
906                    Greatest(p_status(p_idx).from_date, v_from_time)) + 1/86400;
907       v_hours := v_hours + v_time_range / (p_status(p_idx).to_date -
908                                       p_status(p_idx).from_date +
909                                       1/86400) *
910                             CASE p_component_value
911                             WHEN 'RT' THEN Nvl(p_status(p_idx).run_hours, 0)
912                             WHEN 'DT' THEN Nvl(p_status(p_idx).down_hours, 0)
913                             WHEN 'IT' THEN Nvl(p_status(p_idx).idle_hours, 0)
914                             WHEN 'OT' THEN Nvl(p_status(p_idx).off_hours, 0)
915                             ELSE 0
916                             END ;
917              --Dbms_Output.put_line(' case 4 v_hours : ' || v_hours);
918 
919     --  v_from_time := Least(p_status(p_idx).To_Date, p_to_time) + 1/86400;
920       v_continue := TRUE;
921       p_idx := p_idx + 1;
922 
923     END IF;
924   END LOOP;
925   RETURN v_hours;
926 
927   EXCEPTION
928     WHEN OTHERS THEN
929       RETURN 0;
930 END getstatusTimeInOneHour;
931 
932 
933 /* ****************************************************************************
934 * Function	:getCompValuesForEquipStatus                                  *
935 * Description  	:Get run/idle/down/off time for the specified hour. If it     *
936 *                  is not an equipment, return 0.                             *
937 * File Name             :MTHSUSAB.PLS                                         *
938 * Visibility            :Private                                              *
939 * Parameters            :p_entity_fk_key - type of the entity                 *
940 *                        p_entity_type - the ending point of time to          *
941 *                        p_component_value - Component value for shift avail: *
942 *                                                1) Run Time RT               *
943 *                                                2) Down Time DT              *
944 *                                                3) Idle Time IT              *
945 *                                                4) Off Time  OT              *
946 *                        p_from_time - the starting point of time to          *
947 *                                      calculate the virtual meter.           *
948 *                        p_to_time - the ending point of time to              *
949 *                                      calculate the virtual meter            *
950 * Return Value          :Power rating of the equipment. If it is not an       *
951                          equipment, return 0.                                 *
952 **************************************************************************** */
953 
954 FUNCTION getCompValuesForEquipStatus(p_entity_fk_key IN NUMBER,
955                                      p_entity_type IN VARCHAR2,
956                                      p_component_value IN VARCHAR2,
957                                      p_from_time IN DATE,
958                                      p_to_time IN DATE)
959                 RETURN DBMS_SQL.NUMBER_TABLE
960 IS
964     SELECT actual_from_date, actual_to_date, run_hours, down_hours,
961   CURSOR c_getEquipStatusTime(p_equip_fk_key IN VARCHAR2,
962                               p_from_time IN DATE,
963                               p_to_time IN DATE) IS
965            idle_hours, off_hours
966     FROM   MTH_EQUIP_PROD_PERFORMANCE_F
967     WHERE  equipment_fk_key = p_equip_fk_key AND
968            actual_to_date >= p_from_time AND
969            actual_from_date <= p_to_time  AND
970            actual_to_date IS NOT NULL
971     ORDER BY actual_from_date;
972 
973   v_status status_array_type;
974   v_comp_values DBMS_SQL.NUMBER_TABLE;
975 
976   --v_num_elements NUMBER;
977   v_from_time DATE := p_from_time;
978   v_to_time DATE;
979   v_end_from_time DATE := p_to_time - 1/24;
980   v_one_hour NUMBER := 1/24;
981   i NUMBER := 1;
982   --v_value NUMBER;
983   -- Record the current index for the shifts nested table v_shifts
984   v_idx NUMBER := 1;
985   v_continue BOOLEAN := TRUE;
986   v_comp_value NUMBER;
987   v_data_end_time DATE;
988 
989 BEGIN
990 
991   IF (p_entity_type <> 'EQUIPMENT') THEN
992    RETURN v_comp_values;
993   END IF;
994 
995   OPEN c_getEquipStatusTime(p_entity_fk_key,
996                             p_from_time, p_to_time);
997   FETCH c_getEquipStatusTime BULK COLLECT INTO v_status;
998   CLOSE c_getEquipStatusTime;
999 
1000   IF (v_status IS NOT NULL AND v_status.Count > 0) THEN
1001     v_data_end_time := v_status(v_status.Count).to_date;
1002   ELSE
1003     v_data_end_time := v_from_time - 1;
1004   END IF;
1005 
1006   v_end_from_time := Least(v_end_from_time,
1007                             trunc(v_data_end_time - 1/24 + 1/86400, 'HH24'));
1008 
1009     WHILE (v_from_time <= v_end_from_time AND
1010            v_status IS NOT NULL AND
1011            v_idx <= v_status.Count AND
1012            v_continue) LOOP
1013       v_to_time :=  v_from_time + 1/24 - 1/86400;
1014       v_comp_value  := getstatusTimeInOneHour(v_from_time,
1015                                               v_to_time,
1016                                               p_component_value,
1017                                               v_status,
1018                                               v_idx);
1019       IF ( v_comp_value IS NOT NULL) THEN
1020         v_comp_values(i) :=  v_comp_value;
1021         i := i + 1;
1022         v_from_time := v_from_time + v_one_hour;
1023       ELSE
1024         -- The status is not available for this hour
1025         v_continue := FALSE;
1026       END IF;
1027     END LOOP;
1028     RETURN v_comp_values;
1029 
1030 END getCompValuesForEquipStatus;
1031 
1032 
1033 /* ****************************************************************************
1034 * Function       :getMeterReadingInOneHour                                    *
1035 * Description 	 :Get shift availability time in the specified hour for the   *
1036 *                  equipment power rating. If it is not an equipment, return 0*
1037 * File Name             :MTHSUSAB.PLS                                         *
1038 * Visibility            :Private                                              *
1039 * Parameters            :p_from_time - the starting point of time to          *
1040 *                                      calculate the virtual meter.           *
1041 *                        p_to_time - The end time                             *
1042 *                        p_component_value - The possible values are          *
1043 *                                                1) Run Time RT               *
1044 *                                                2) Down Time DT              *
1045 *                                                3) Idle Time IT              *
1046 *                                                4) Off Time  OT              *
1047 *                        p_status - nested table contains the status times    *
1048 *                        p_idx - current index to the status table            *
1049 * Return Value          :Time in hour for the for the specified status        *
1050 *                         within the hour specified;                          *
1051                          NULL if the status is not available for the          *
1052 *                        specified time range. Need to wait for the status    *
1053 *                        to be available.                                     *
1054 **************************************************************************** */
1055 
1056 FUNCTION getMeterReadingInOneHour(p_from_time IN DATE,
1057                                   p_to_time IN DATE,
1058                                   p_meter_fk_key IN VARCHAR2,
1059                                   p_meter_readings IN readings_array_type,
1060                                   p_idx IN OUT NOCOPY NUMBER)
1061            RETURN NUMBER
1062 IS
1063   v_usage_value NUMBER := 0;
1064   v_time_range NUMBER := 0;
1065   v_continue BOOLEAN := TRUE;
1066   v_from_time DATE := p_from_time;
1067   v_meter_value NUMBER;
1068 BEGIN
1069   WHILE (p_idx <= p_meter_readings.Count AND v_from_time < p_to_time AND
1070          v_continue) LOOP
1071 
1072     IF (p_to_time > p_meter_readings(p_idx).from_date AND
1073         p_meter_readings(p_idx).To_Date IS NULL) THEN
1074      -- The meter range is still open without ending time. Need to wait until
1075      -- the status range is closed in order to calculate the hours for the
1076      -- status
1077       -- from time, to time:     |----------------|
1078       -- status start and end:           |-------------------...(end time=NULL)
1079       v_usage_value := NULL;
1080       v_continue := FALSE;
1081     ELSIF (p_to_time < p_meter_readings(p_idx).from_date) THEN
1082       -- No more status for the time range  since the current status range
1083       -- is beyond the time range
1084       -- from time, to time:     |----------------|
1088     ELSIF  (v_from_time > p_meter_readings(p_idx).to_date) THEN
1085       -- status start and end:                         |---------------------|
1086 
1087       v_continue := FALSE;
1089       -- The current time range is ahead of the status range, move the status
1090       -- range forward
1091       -- from time, to time:                               |----------------|
1092       -- status start and end:    |---------------------|
1093 
1094       p_idx := p_idx + 1;
1095       -- there is intersection between the status range and the time range
1096     ELSIF (p_to_time >= p_meter_readings(p_idx).from_date AND
1097            p_to_time <= p_meter_readings(p_idx).To_Date) THEN
1098       -- to_time is within the range of the status range
1099       -- from time, to time:       |----------------|
1100       -- status start and end:          |----------------------------|
1101       -- or
1102       -- from time, to time:              |----------------|
1103       -- status start and end:          |----------------------------|
1104       -- time range overlaps with status, but does not include end of status
1105 
1106       v_time_range := (Least(p_meter_readings(p_idx).To_Date, p_to_time) -
1107                        Greatest(p_meter_readings(p_idx).from_date, v_from_time))
1108                           + 1/86400;
1109       v_usage_value := v_usage_value +
1110                         v_time_range / (p_meter_readings(p_idx).to_date -
1111                                       p_meter_readings(p_idx).from_date +
1112                                       1/86400) *
1113                                       p_meter_readings(p_idx).usage_value;
1114       v_from_time := Least(p_meter_readings(p_idx).To_Date, p_to_time) +
1115                       1/86400;
1116       v_continue := FALSE;
1117 
1118     ELSIF (p_from_time <= p_meter_readings(p_idx).from_date AND
1119            p_to_time >= p_meter_readings(p_idx).To_Date) THEN
1120       -- The status record is within the time range
1121       -- from time, to time:         |--------------------------------|
1122       -- shift start and end:            |------------------|
1123       --v_time_range := (Least(p_status(p_idx).To_Date, p_to_time) -
1124       --           Greatest(p_status(p_idx).from_date, v_from_time)) + 1/86400;
1125 
1126      -- v_hours := v_hours + v_time_range / (p_status(p_idx).to_date -
1127       --                                p_status(p_idx).from_date +
1128       --                                1/86400) *
1129       v_usage_value := v_usage_value + p_meter_readings(p_idx).usage_value;
1130       v_from_time := Least(p_meter_readings(p_idx).To_Date, p_to_time) +
1131                       1/86400;
1132       v_continue := TRUE;
1133       p_idx := p_idx + 1;
1134 
1135     ELSIF (p_from_time >= p_meter_readings(p_idx).from_date AND
1136            p_from_time <= p_meter_readings(p_idx).To_Date) THEN
1137       -- from time is within the range of the status range, but not the to time.
1138       -- from time, to time:                         |----------------|
1139       -- status start and end:          |---------------------|
1140       -- time range overlaps with status, but does not include the end of status
1141       v_time_range := (Least(p_meter_readings(p_idx).To_Date, p_to_time) -
1142                      Greatest(p_meter_readings(p_idx).from_date, v_from_time)) +
1143                        1/86400;
1144       v_usage_value := v_usage_value +
1145                        v_time_range / (p_meter_readings(p_idx).to_date -
1146                                       p_meter_readings(p_idx).from_date +
1147                                       1/86400) *
1148                                       p_meter_readings(p_idx).usage_value;
1149 
1150       v_from_time := Least(p_meter_readings(p_idx).To_Date, p_to_time) +
1151                      1/86400;
1152 
1153       v_continue := TRUE;
1154       --ELSE
1155             --Dbms_Output.put_line(' case 4 p_idx : ' || p_idx);
1156 
1157     END IF;
1158   END LOOP;
1159   RETURN v_usage_value;
1160 END getMeterReadingInOneHour;
1161 
1162 
1163 /* ****************************************************************************
1164 * Function	:getCompValuesForMeter                                        *
1165 * Description 	:Get run/idle/down/off time for the specified hour. If it     *
1166 *                  is not an equipment, return 0.                             *
1167 * File Name             :MTHSUSAB.PLS                                         *
1168 * Visibility            :Private                                              *
1169 * Parameters            :p_meter_fk_key - meter fk key                        *
1170 *                        p_entity_type - the ending point of time to          *
1171 *                        p_component_value - meter fk key                     *
1172 *                        p_from_time - the starting point of time to          *
1173 *                                      calculate the virtual meter.           *
1174 *                        p_to_time - the ending point of time to              *
1175 *                                      calculate the virtual meter            *
1176 * Return Value          :Array of meter readings with type readings_array_type*
1177 **************************************************************************** */
1178 
1179 FUNCTION getCompValuesForMeter(p_meter_fk_key IN NUMBER,
1180                                p_from_time IN DATE,
1181                                p_to_time IN DATE)
1182                 RETURN DBMS_SQL.NUMBER_TABLE
1183 IS
1184   CURSOR c_getMeterReadings(p_meter_fk_key IN NUMBER,
1185                             p_from_time IN DATE,
1186                             p_to_time IN DATE) IS
1187 
1188     SELECT from_time, to_time, usage_value
1189     FROM   mth_meter_readings
1190     WHERE  meter_fk_key = p_meter_fk_key AND
1191            to_time >= p_from_time AND
1192            from_time <= p_to_time
1193     ORDER BY from_time;
1194 
1195   v_meter_readings readings_array_type;
1199   v_from_time DATE := p_from_time;
1196   v_comp_values DBMS_SQL.NUMBER_TABLE;
1197 
1198   v_num_elements NUMBER;
1200   v_to_time DATE;
1201   v_end_from_time DATE := p_to_time - 1/24;
1202   v_one_hour NUMBER := 1/24;
1203   i NUMBER := 1;
1204   v_value NUMBER;
1205   -- Record the current index for the shifts nested table v_shifts
1206   v_idx NUMBER := 1;
1207   v_continue BOOLEAN := TRUE;
1208   v_comp_value NUMBER;
1209   v_data_end_time DATE;
1210 
1211 
1212 BEGIN
1213   OPEN c_getMeterReadings(p_meter_fk_key, p_from_time, p_to_time);
1214   FETCH c_getMeterReadings BULK COLLECT INTO v_meter_readings;
1215   CLOSE c_getMeterReadings;
1216 
1217   IF (v_meter_readings IS NOT NULL AND v_meter_readings.Count > 0) THEN
1218     v_data_end_time := v_meter_readings(v_meter_readings.Count).To_Date;
1219   ELSE
1220     v_data_end_time := v_from_time - 1;
1221   END IF;
1222   --Dbms_Output.put_line( ' v_data_end_time ' || v_data_end_time);
1223 
1224     --Dbms_Output.put_line( ' p_to_time ' || p_to_time);
1225 
1226   v_end_from_time := Least(v_end_from_time,
1227                             trunc(v_data_end_time - 1/24 + 1/86400, 'HH24'));
1228   --Dbms_Output.put_line( ' v_end_from_time ' || v_end_from_time);
1229 
1230   v_num_elements :=  Trunc((p_to_time - p_from_time) * 24) + 1;
1231   WHILE (v_from_time <= v_end_from_time AND
1232          v_meter_readings IS NOT NULL AND
1233          v_idx <= v_meter_readings.Count AND
1234          v_continue) LOOP
1235     v_to_time :=  v_from_time + 1/24 - 1/86400;
1236     v_comp_value  := getMeterReadingInOneHour(v_from_time,
1237                                               v_to_time,
1238                                               p_meter_fk_key,
1239                                               v_meter_readings,
1240                                               v_idx);
1241 
1242 
1243     -- TO DO: Need to check whether that is the last reading and the
1244     -- end time of the last record is less than the from time
1245     --- If so, need to stop there.
1246     IF ( v_comp_value IS NOT NULL) THEN
1247       v_comp_values(i) :=  v_comp_value;
1248       i := i + 1;
1249       v_from_time := v_from_time + v_one_hour;
1250     ELSE
1251       -- The status is not available for this hour
1252       v_continue := FALSE;
1253     END IF;
1254     END LOOP;
1255 
1256     RETURN v_comp_values;
1257 END getCompValuesForMeter;
1258 
1259 
1260 /* ****************************************************************************
1261 * Function	:getCompValuesForCustom                                       *
1262 * Description  	:Call custom API to get the usage value for custom type       *
1263 * File Name             :MTHSUSAB.PLS                                         *
1264 * Visibility            :Private                                              *
1265 * Parameters            :p_entity_fk_key - type of the entity                 *
1266 *                        p_entity_type - the ending point of time to          *
1267 *                        p_component_value - Component value                  *
1268 *                        p_from_time - the starting point of time to          *
1269 *                                      calculate the virtual meter.           *
1270 *                        p_to_time - the ending point of time to              *
1271 *                                      calculate the virtual meter            *
1272 * Return Value          :Usage value for each hour for the time duration      *
1273 *                        specified                                            *
1274 **************************************************************************** */
1275 
1276 FUNCTION getCompValuesForCustom(p_entity_fk_key IN NUMBER,
1277                                 p_entity_type IN VARCHAR2,
1278                                 p_component_value IN VARCHAR2,
1279                                 p_from_time IN DATE,
1280                                 p_to_time IN DATE)
1281                 RETURN DBMS_SQL.NUMBER_TABLE
1282 IS
1283   CURSOR c_get_custom_api IS
1284 
1285     SELECT DESCRIPTION
1286     FROM FND_LOOKUPS
1287     WHERE LOOKUP_CODE = 'CUSTOM_SUSTAIN_ASPECT_API' AND
1288           LOOKUP_TYPE ='MTH_CUSTOM_PLSQL_API';
1289 
1290   v_api_name VARCHAR2(200) := NULL;
1291   v_stmt VARCHAR2(4000);
1292   v_comp_values DBMS_SQL.NUMBER_TABLE;
1293 
1294   v_num_elements NUMBER;
1295   v_from_time DATE := p_from_time;
1296   v_to_time DATE;
1297   v_end_from_time DATE := p_to_time - 1/24;
1298   v_one_hour NUMBER := 1/24;
1299   i NUMBER := 1;
1300   v_value NUMBER;
1301   -- Record the current index for the shifts nested table v_shifts
1302   v_idx NUMBER := 1;
1303   v_continue BOOLEAN := TRUE;
1304   v_comp_value NUMBER;
1305   v_err_msg VARCHAR2(4000);
1306   v_err_code NUMBER;
1307 
1308 
1309 BEGIN
1310     SELECT DESCRIPTION INTO v_api_name
1311     FROM FND_LOOKUPS
1312     WHERE LOOKUP_CODE = 'CUSTOM_SUSTAIN_ASPECT_API' AND
1313           LOOKUP_TYPE ='MTH_CUSTOM_PLSQL_API';
1314 
1315   IF (sql%NOTFOUND) THEN
1316 
1317     v_err_msg :=
1318     'Could not find the custom API to calculate sustainability aspect usage';
1319 
1320     INSERT INTO mth_runtime_err
1321       ( MODULE, error_msg, timestamp) VALUES
1322        ('MTH_SUSTAIN_ASPECT_PKG.getCompValuesForCustom', v_err_msg, SYSDATE);
1323   ELSE
1324   -- API signature:
1325   -- v_api_name(p_entity_fk_key IN NUMBER,
1326   --                              p_entity_type IN VARCHAR2,
1327   --                              p_component_value IN VARCHAR2,
1328   --                              p_from_time IN DATE,
1329   --                              p_to_time IN DATE)
1330     v_stmt := 'SELECT ' ||  v_api_name || '(:1, ' ||       -- p_entity_type
1331                                            ':2, ' ||       -- p_entity_type
1335                               ' FROM DUAL';
1332                                            ':3, ' ||       -- p_component_value
1333                                            ':4, ' ||       -- p_from_time
1334                                            ':5)'  ||       -- p_to_time
1336                               --Dbms_Output.put_line(' v_stmt ' || v_stmt);
1337     v_num_elements :=  Trunc((p_to_time - p_from_time) * 24) + 1;
1338     WHILE (v_from_time <= v_end_from_time AND v_continue) LOOP
1339       v_to_time :=  v_from_time + 1/24 - 1/86400;
1340       EXECUTE IMMEDIATE v_stmt INTO v_comp_value USING p_entity_fk_key,
1341                                                        p_entity_type,
1342                                                        p_component_value,
1343                                                        v_from_time,
1344                                                        v_to_time;
1345 
1346 
1347 
1348       -- TO DO: Need to check whether that is the last reading and the
1349       -- end time of the last record is less than the from time
1350       --- If so, need to stop there.
1351       IF ( v_comp_value IS NOT NULL) THEN
1352         v_comp_values(i) :=  v_comp_value;
1353         i := i + 1;
1354         v_from_time := v_from_time + v_one_hour;
1355       ELSE
1356         -- The status is not available for this hour
1357         v_continue := FALSE;
1358       END IF;
1359       END LOOP;
1360 
1361   END IF;
1362 
1363   RETURN v_comp_values;
1364 
1365   EXCEPTION
1366     WHEN OTHERS THEN
1367       v_err_msg := SQLERRM;
1368       v_err_code := SQLCODE;
1369 
1370     INSERT INTO mth_runtime_err
1371       ( MODULE, error_code, error_msg, timestamp) VALUES
1372        ('MTH_SUSTAIN_ASPECT_PKG.getCompValuesForCustom',
1373         v_err_code, v_err_msg, SYSDATE);
1374 
1375   RETURN v_comp_values;
1376 
1377 END getCompValuesForCustom;
1378 
1379 
1380 /* ****************************************************************************
1381 * Function	:calculate_virtual_component                                  *
1382 * Description  	:Calculate the virtual component for the specified time       *
1383 *                  in every hour                                              *
1384 * File Name             :MTHSUSAB.PLS                                         *
1385 * Visibility            :Private                                              *
1386 * Parameters            :p_component_type - component type  could be:         *
1387 *                         MTH_METER_BASED_COMPONENT	Constant CONST        *
1388 *                         MTH_METER_BASED_COMPONENT	Meter	METER         *
1389 *                         MTH_PWR_COMPONENT	Entity Power Rating	EPR   *
1390 *                         MTH_PWR_COMPONENT	Equipment Status	ES    *
1391 *                         MTH_PWR_COMPONENT	Shift Availibility	SFT   *
1392 *                         MTH_CUST_COMPONENT	Custom Component	CC    *
1393 *                        p_component_value - Component value depends on type  *
1394 *                                                1) Meter Based: MB           *
1395 *                                                2) Power rating Based: RPB   *
1396 *                                                3) Custom : CB               *
1397 *                        p_entity_type - Associated entity type for calcuation*
1398 *                        p_entity_fk_key - Assocuated entity fk key           *
1399 *                        p_from_time - the starting point of time to          *
1400 *                                      calculate the virtual meter.           *
1401 *                        p_to_time - the ending point of time to              *
1402 *                                      calculate the virtual meter            *
1403 * Return Value          :List of values of the components for each hour       *
1404 *                        within the from time and to time. The type of the    *
1405 *                        return value is DBMS_SQL.NUMBER_TABLE                *
1406 **************************************************************************** */
1407 
1408 FUNCTION calculate_virtual_component(p_component_type IN VARCHAR2,
1409                                      p_component_value IN VARCHAR2,
1410                                      p_entity_type IN VARCHAR2,
1411                                      p_entity_fk_key IN VARCHAR2,
1412                                      p_from_time IN DATE,
1413                                      p_to_time IN DATE)
1414            RETURN DBMS_SQL.NUMBER_TABLE
1415 
1416 IS
1417   v_comp_values DBMS_SQL.NUMBER_TABLE;
1418   v_num_elements NUMBER;
1419   v_from_time DATE := p_from_time;
1420   v_end_from_time DATE := p_to_time - 1/24;
1421   v_one_hour NUMBER := 1/24;
1422   i NUMBER := 1;
1423   v_value NUMBER;
1424 BEGIN
1425   v_num_elements :=  Trunc((p_to_time - p_from_time) * 24) + 1;
1426  -- v_comp_values :=  DBMS_SQL.NUMBER_TABLE(v_num_elements);
1427   IF (p_component_type = 'CONST') THEN
1428     -- 1. Support CONST type component
1429     v_value := To_Number(p_component_value);
1430     WHILE (v_from_time <= v_end_from_time) LOOP
1431       v_comp_values(i) := v_value;
1432       i := i + 1;
1433       v_from_time := v_from_time + v_one_hour;
1434     END LOOP;
1435   ELSIF (p_component_type = 'EPR') THEN
1436     -- 2. Support Entity Power Rating type component
1437     v_value := getEquipPowerRating(p_entity_fk_key, p_entity_type);
1438     WHILE (v_from_time <= v_end_from_time) LOOP
1439       v_comp_values(i) := v_value;
1440       i := i + 1;
1441       v_from_time := v_from_time + v_one_hour;
1442     END LOOP;
1443   ELSIF (p_component_type = 'SFT') THEN
1444     -- 3. Support Shift Availibility type componen
1445     v_comp_values := getCompValuesForSFT(p_entity_fk_key,
1446                                          p_entity_type,
1447                                          p_component_value,
1448                                          p_from_time,
1452     v_comp_values := getCompValuesForEquipStatus(p_entity_fk_key,
1449                                          p_to_time);
1450   ELSIF (p_component_type = 'ES') THEN
1451     -- 4. Support Equipment Status type componen
1453                                                  p_entity_type,
1454                                                  p_component_value,
1455                                                  p_from_time,
1456                                                  p_to_time);
1457 
1458   ELSIF (p_component_type = 'METER') THEN
1459     -- 5. Support Meter type componen
1460     -- getCompValuesForMeter
1461 
1462     v_comp_values := getCompValuesForMeter(To_Number(p_component_value),
1463                                            p_from_time,
1464                                            p_to_time);
1465   ELSIF (p_component_type = 'CC') THEN
1466     -- 6. Support Custom Component type componen
1467     v_comp_values := getCompValuesForCustom(p_entity_fk_key,
1468                                             p_entity_type,
1469                                             p_component_value,
1470                                             p_from_time,
1471                                             p_to_time);
1472 
1473   END IF;
1474 
1475 
1476   RETURN v_comp_values;
1477 END calculate_virtual_component;
1478 
1479 
1480 /* ****************************************************************************
1481 * Procedure		:cal_virtual_meter_component                          *
1482 * Description 	 	:Calculate all the virtual meters that is power rating*
1483 *                  based hourly if the components are available and           *
1484 *                  load the data into meter readings table MTH_METER_READINGS *
1485 * File Name             :MTHSUSAB.PLS                                         *
1486 * Visibility            :Private                                              *
1487 * Parameters            :p_meter_pk_key - meter pk key                        *
1488 *                        p_virtual_meter_type - virtual meter type. It can be *
1489 *                                                1) Meter Based: MB           *
1490 *                                                2) Powering rating Based: RPB*
1491 *                                                3) Custom : CB               *
1492 *                        p_virtual_meter_formula - Formula to calculate meter *
1493 *                        p_entity_type - Associated entity type for calcuation*
1494 *                        p_entity_fk_key - Assocuated entity fk key           *
1495 *                        p_from_time - the starting point of time to          *
1496 *                                      calculate the virtual meter.           *
1497 *                        p_to_time - the ending point of time to              *
1498 *                                      calculate the virtual meter            *
1499 * Return Value          :None                                                 *
1500 **************************************************************************** */
1501 
1502 FUNCTION cal_virtual_meter_component(p_meter_pk_key IN NUMBER,
1503                                      p_virtual_meter_type IN VARCHAR2,
1504                                      p_virtual_meter_formula IN VARCHAR2,
1505                                      p_entity_type IN VARCHAR2,
1506                                      p_entity_fk_key IN VARCHAR2,
1507                                      p_from_time IN DATE,
1508                                      p_to_time IN DATE)
1509            RETURN component_lookup_type
1510 IS
1511   CURSOR c_getMeterComponents(p_meter_fk_key IN NUMBER) IS
1512     SELECT virtual_meter_component_pk_key as component_pk_key,
1513            component_type, component_value
1514     FROM MTH_VIRTUAL_METER_COMPONENTS
1515     WHERE  meter_fk_key = p_meter_fk_key;
1516 
1517   v_one_comp component_record;
1518   v_compoents component_lookup_type;
1519   i NUMBER := 1;
1520 
1521 BEGIN
1522    -- 1. Get all the virtual component components
1523    FOR r_comp IN  c_getMeterComponents(p_meter_pk_key) LOOP
1524      v_one_comp.component_pk_key := r_comp.component_pk_key;
1525      v_one_comp.component_type := r_comp.component_type;
1526      v_one_comp.component_value := r_comp.component_value;
1527      v_one_comp.comp_time_series_values :=
1528                       calculate_virtual_component(r_comp.component_type,
1529                                                   r_comp.component_value,
1530                                                   p_entity_type,
1531                                                   p_entity_fk_key,
1532                                                   p_from_time,
1533                                                   p_to_time);
1534 
1535      v_compoents(i) := v_one_comp;
1536      i := i + 1;
1537    END LOOP;
1538 
1539    RETURN v_compoents;
1540 END cal_virtual_meter_component;
1541 
1542 
1543 
1544 /* ****************************************************************************
1545 * Function	:calVirtualMeter                                              *
1546 * Description  	:Calculate the virtual meter using the formula with all the   *
1547 *                  components available for pne hour                          *
1548 * File Name             :MTHSUSAB.PLS                                         *
1549 * Visibility            :Private                                              *
1550 * Parameters            :p_formula - Virtual meter formula                    *
1551 *                        p_components - All the components with all their     *
1552 *                           values for each component                         *
1553 *                        p_idx - The index in p_components that indicates     *
1554 *                           the set of components to be calculated.           *
1555 * Return Value          :Usage value calcuated using the formula              *
1556 **************************************************************************** */
1557 
1561 IS
1558 FUNCTION calVirtualMeter(p_cursor IN NUMBER, -- p_formula IN VARCHAR2,
1559                          p_components IN component_lookup_type,
1560                          p_idx IN NUMBER) RETURN NUMBER
1562   v_usageValue NUMBER := 0;
1563   v_stmt VARCHAR2(4000);
1564   c NUMBER;
1565   dummy NUMBER;
1566 BEGIN
1567 
1568 
1569   -- 1. Construct the dynamic sql statement to calculate virtual meter formula
1570   DBMS_SQL.DEFINE_COLUMN(p_cursor, 1, v_usageValue);
1571 
1572   FOR i IN 1..p_components.Count LOOP
1573     DBMS_SQL.BIND_VARIABLE(p_cursor, ':' || i,
1574                            p_components(i).comp_time_series_values(p_idx));
1575   END LOOP;
1576 
1577   dummy := DBMS_SQL.EXECUTE(p_cursor);
1578   IF DBMS_SQL.FETCH_ROWS(p_cursor)>0 THEN
1579          -- get column values of the row
1580       --     Dbms_Output.put_line( ' DBMS_SQL.FETCH_ROWS(c)>0 : ' );
1581          DBMS_SQL.COLUMN_VALUE(p_cursor, 1, v_usageValue);
1582   END IF;
1583 
1584   --DBMS_SQL.CLOSE_CURSOR(c);
1585   RETURN  v_usageValue;
1586    /*
1587   EXCEPTION WHEN OTHERS THEN
1588     IF DBMS_SQL.IS_OPEN(c) THEN
1589       DBMS_SQL.CLOSE_CURSOR(c);
1590     END IF;
1591 
1592    RETURN v_usageValue;
1593        */
1594 
1595 END calVirtualMeter;
1596 
1597 
1598 /* ****************************************************************************
1599 * Procedure   :cal_save_virtual_meter                                         *
1600 * Description  	  :Calculate  virtual meter using the component values   and  *
1601 *                  load the data into meter readings table MTH_METER_READINGS *
1602 * File Name             :MTHSUSAB.PLS                                         *
1603 * Visibility            :Private                                              *
1604 * Parameters            :p_meter_pk_key - meter pk key                        *
1605 *                        p_formula - Virtual meter formula to calculate meter *
1606 *                        p_components - All the components with all their     *
1607 *                           values for each component                         *
1608 *                        p_virtual_meter_formula - Formula to calculate meter *
1609 *                        p_entity_type - Associated entity type for calcuation*
1610 *                        p_entity_fk_key - Assocuated entity fk key           *
1611 *                        p_start_time - the starting point of time to         *
1612 *                                      calculate the virtual meter.           *
1613 *                        p_end_time - the ending point of time to             *
1614 *                                      calculate the virtual meter            *
1615 *                        p_bulk_commit_size - max commit size                 *
1616 *                        p_num_trans  - current number of insert/update       *
1617 * Return Value          :None                                                 *
1618 **************************************************************************** */
1619 PROCEDURE   cal_save_virtual_meter(p_meter_fk_key IN NUMBER,
1620                                    p_formula IN VARCHAR2,
1621                                    p_components IN component_lookup_type,
1622                                    p_start_time IN DATE,
1623                                    p_end_time IN DATE)
1624 
1625 IS
1626   v_numElements NUMBER := NULL;
1627   v_usage_value NUMBER;
1628   v_hour_start_time DATE := p_start_time;
1629   v_hour_end_time DATE;
1630   v_system_id NUMBER := -99999;
1631   v_stmt VARCHAR2(4000);
1632   c NUMBER;
1633   v_err_msg VARCHAR2(4000);
1634   v_err_code NUMBER;
1635 BEGIN
1636   -- 1. Find out the number of entries to be inserted, which should be the
1637   --    the least one among them
1638   FOR i IN 1..p_components.Count LOOP
1639     IF (v_numElements IS NULL OR
1640         p_components(i).comp_time_series_values.Count < v_numElements) THEN
1641       v_numElements := p_components(i).comp_time_series_values.Count;
1642 
1643     END IF;
1644   END LOOP;
1645 
1646   IF (v_numElements IS NULL) THEN
1647     v_numElements := 0;
1648   END IF;
1649 
1650   -- 2. Construct the sql statement
1651   v_stmt := 'SELECT ' || p_formula || ' FROM (SELECT ';
1652   FOR i IN 1..p_components.Count LOOP
1653     IF (i > 1) THEN
1654       v_stmt := v_stmt || ' ,';
1655     END IF;
1656     v_stmt := v_stmt || ':' || i || ' AS ID#' ||
1657                        p_components(i).component_pk_key;
1658   END LOOP;
1659   v_stmt := v_stmt || ' FROM DUAL)';
1660 
1661   c := DBMS_SQL.OPEN_CURSOR;
1662   DBMS_SQL.PARSE(c, v_stmt, DBMS_SQL.NATIVE);
1663 
1664   -- 2. Calculate the virtual meter for each hour available
1665   FOR i IN 1..v_numElements LOOP
1666     v_usage_value := calVirtualMeter(c, p_components, i);
1667     v_hour_end_time := v_hour_start_time + 1/24 - 1/86400;
1668     INSERT INTO mth_meter_readings
1669                   (meter_fk_key, from_time, to_time, usage_value,
1670                    creation_date, last_update_date, creation_system_id,
1671                    last_update_system_id, created_by, last_updated_by,
1672                    last_update_login, processed_flag) VALUES
1673                   (p_meter_fk_key, v_hour_start_time, v_hour_end_time,
1674                    v_usage_value, SYSDATE, SYSDATE, v_system_id,
1675                    v_system_id, v_system_id, v_system_id, v_system_id, 'N');
1676     v_hour_start_time := v_hour_start_time + 1/24;
1677 
1678 
1679   END LOOP;
1680     DBMS_SQL.CLOSE_CURSOR(c);
1681 
1682   EXCEPTION
1683     WHEN OTHERS THEN
1684     --  v_err_code := 'Failed to calculate the virtual meter between ' ||
1685     --        To_Char(p_start_time, 'YYYY-MM-DD HH24:MI:SS') || ' and '  ||
1686     --        To_Char(p_end_time, 'YYYY-MM-DD HH24:MI:SS') ||
1687     --        '. The error code is ' || SQLCODE ||
1691     INSERT INTO mth_runtime_err
1688     --        ' and the error message is ' || SQLERRM || '.';
1689       v_err_code := SQLCODE;
1690       v_err_msg :=  SQLERRM;
1692       ( MODULE, error_code, error_msg, timestamp) VALUES
1693        ('MTH_SUSTAIN_ASPECT_PKG.cal_save_virtual_meter',
1694         v_err_code, v_err_msg, SYSDATE);
1695 
1696        DBMS_SQL.CLOSE_CURSOR(c);
1697 
1698 
1699 END cal_save_virtual_meter;
1700 
1701 
1702 
1703 
1704 /* ****************************************************************************
1705 * Procedure		:ADD_VRT_TO_METER_READINGS                            *
1706 * Description 	  :Calculate all the virtual meters if possible hourly and    *
1707 * load the data into meter readings table MTH_METER_READINGS                  *
1708 * File Name             :MTHSUSAB.PLS                                         *
1709 * Visibility            :Private                                              *
1710 * Parameters            :None                                                 *
1711 * Return Value          :None                                                 *
1712 **************************************************************************** */
1713 
1714 PROCEDURE ADD_VRT_MTS_TO_METER_READINGS (p_virtual_meter_start_date IN DATE
1715                                              DEFAULT VIRTUAL_METER_START_DATE)
1716 IS
1717   -- Get all the virtual meters order by precedence
1718   -- Fixed bug 9965797 to POPULATE THE METER READINGS FOR EACH METER ONLY ONCE
1719   -- by adding DISTINCT in the SELECT statement below
1720   CURSOR c_getVirtualMeters  IS
1721     SELECT DISTINCT M.METER_PK_KEY, M.VIRTUAL_METER_TYPE,
1722            M.VIRTUAL_METER_FORMULA,
1723            M.PRECEDENCE, E.ENTITY_FK_KEY, UPPER(E.ENTITY_TYPE) ENTITY_TYPE,
1724            R.END_TIME
1725     FROM MTH_METERS M, MTH_METER_ENTITIES  E,
1726          (SELECT meter_fk_key, Max(to_time) end_time
1727           FROM   MTH_METER_READINGS
1728           GROUP BY meter_fk_key) R
1729     WHERE M.METER_TYPE = 'VRT' AND M.METER_PK_KEY = E.METER_FK_KEY (+) AND
1730           M.METER_PK_KEY = r.meter_fk_key (+)
1731     ORDER BY M.PRECEDENCE;
1732 
1733   v_start_time DATE;
1734   v_end_time DATE;
1735   v_compoents component_lookup_type;
1736   v_num_trans NUMBER := 0;
1737 
1738 BEGIN
1739   -- 1. Get all the virtual meters order by their precedence to
1740   --    to calculate the virtual meters
1741   v_end_time :=  trunc(SYSDATE, 'HH24');
1742   FOR v_meter IN c_getVirtualMeters LOOP
1743     -- 1.1 Calculate the virtual meter only if it has passed at least
1744     --     one hour from last reading
1745     --v_start_time := Nvl(v_meter.END_TIME, p_virtual_meter_start_date) + 1/2600;
1746     v_start_time := Nvl(v_meter.END_TIME, p_virtual_meter_start_date) + 1/23600;
1747     v_start_time := Trunc(v_start_time, 'HH24');
1748     IF (v_meter.END_TIME IS NOT NULL AND v_start_time <= v_meter.END_TIME) THEN
1749       v_start_time := v_start_time + 1/24;
1750     END IF;
1751     IF (v_end_time > trunc(v_start_time + 1/24, 'HH24')) THEN
1752 
1753       v_compoents :=  cal_virtual_meter_component(v_meter.METER_PK_KEY,
1754                                                   v_meter.VIRTUAL_METER_TYPE,
1755                                                   v_meter.VIRTUAL_METER_FORMULA,
1756                                                   v_meter.ENTITY_TYPE,
1757                                                   v_meter.ENTITY_FK_KEY,
1758                                                   v_start_time,
1759                                                   v_end_time);
1760 
1761 
1762     END IF;
1763 
1764     -- 1.2 Calculate and save the meter values
1765     cal_save_virtual_meter(v_meter.METER_PK_KEY,
1766                            v_meter.VIRTUAL_METER_FORMULA,
1767                            v_compoents,
1768                            v_start_time,
1769                            v_end_time);
1770   END LOOP;
1771 
1772 END ADD_VRT_MTS_TO_METER_READINGS;
1773 
1774 PROCEDURE init_hour_reading_table
1775 AS
1776 
1777 BEGIN
1778   p_reading := MeterReadingTable(NULL,NULL,NULL,NULL,NULL);
1779 END init_hour_reading_table;
1780 
1781 PROCEDURE init_shift_break_table
1782 AS
1783 
1784 BEGIN
1785   p_shift := EntityShiftTable(NULL,NULL,NULL,NULL,NULL,NULL,NULL);
1786 END init_shift_break_table;
1787 
1788 FUNCTION break_hours RETURN MeterReadingTable
1789 AS
1790    CURSOR cur_meter
1791    IS
1792       SELECT a.meter_fk_key, a.from_time, a.to_time, a.usage_value, b.meter_code
1793       FROM mth_meter_readings a, mth_meters b
1794       WHERE a.meter_fk_key = b.meter_pk_key
1795       AND a.processed_flag = 'N'
1796       ORDER BY a.meter_fk_key, a.from_time;
1797 
1798    l_hour_count NUMBER;
1799    i NUMBER := 1;
1800 
1801 BEGIN
1802    init_hour_reading_table;
1803    FOR reading_rec in cur_meter LOOP
1804       --SELECT round ((reading_rec.to_time - reading_rec.from_time) *24)
1805       SELECT (Trunc(reading_rec.to_time, 'hh24') - Trunc(reading_rec.from_time, 'hh24')) *24
1806       INTO l_hour_count
1807       FROM DUAL;
1808 
1809      FOR idx in 0..l_hour_count LOOP
1810          p_reading.EXTEND(1);
1811          BEGIN
1812             SELECT reading_rec.meter_fk_key,
1813                  h.hour_pk_key,
1814                  (CASE idx
1815                      WHEN 0 THEN reading_rec.from_time
1816                        ELSE h.from_time
1817                      END) reading_from_time,
1818                  (CASE idx
1819                        WHEN l_hour_count THEN reading_rec.to_time
1820                        ELSE h.to_time
1821                      END) reading_to_time,
1822                   (((CASE idx
1823                        WHEN l_hour_count THEN reading_rec.to_time
1824                        ELSE h.to_time
1828                         ELSE h.from_time
1825                      END) -
1826                    (CASE idx
1827                        WHEN 0 THEN reading_rec.from_time
1829                      END))*24*60*60+1)
1830                   /((reading_rec.to_time - reading_rec.from_time)*24*60*60+1)
1831                   *reading_rec.usage_value
1832             INTO p_reading(i).meter_fk_key, p_reading(i).hour_pk_key, p_reading(i).from_time, p_reading(i).to_time, p_reading(i).usage_value
1833             FROM mth_hour_d h
1834             WHERE Trunc(reading_rec.from_time, 'hh24') + idx/24 = h.from_time;
1835             i := i+1;
1836 
1837         EXCEPTION
1838             WHEN No_Data_Found THEN
1839                 IF SQL%NOTFOUND THEN
1840                    INSERT INTO mth_meter_readings_err
1841                       (meter_readings_err_pk_key, meter_code, from_time, to_time, usage_value, tag_code, reprocess_ready_yn,
1842                        err_code, creation_date, last_update_date)
1843                    VALUES (MTH_METER_READINGS_ERR_S.NEXTVAL, reading_rec.meter_code, reading_rec.from_time, reading_rec.to_time,
1844                            reading_rec.usage_value, NULL, 'N', 'HRNA', SYSDATE, SYSDATE);
1845                 END IF;
1846                 EXIT;
1847             WHEN OTHERS THEN
1848               ROLLBACK;
1849               raise_application_error(-20000,'Error occurs when inserting error table:'||SQLCODE||' -ERROR- '||SQLERRM);
1850         END;
1851 
1852      END LOOP;
1853 
1854      BEGIN
1855         UPDATE mth_meter_readings SET processed_flag = 'Y'
1856         WHERE meter_fk_key = reading_rec.meter_fk_key
1857         AND from_time = reading_rec.from_time;
1858      EXCEPTION
1859         WHEN OTHERS THEN
1860             ROLLBACK;
1861             raise_application_error(-20001,'Error occurs when update process flag:'||SQLCODE||' -ERROR- '||SQLERRM);
1862      END;
1863    END LOOP;
1864 
1865    RETURN p_reading;
1866 
1867 END break_hours;
1868 
1869 PROCEDURE break_reading
1870 AS
1871 
1872   j NUMBER := 1;
1873   l NUMBER := 1;
1874   l_duration NUMBER;
1875   l_entity_fk_key mth_meter_entities.entity_fk_key%TYPE;
1876   l_shift_rec_to_time mth_meter_readings.to_time%TYPE;
1877   l_new_from_time mth_meter_readings.from_time%TYPE;
1878   l_to_time mth_meter_readings.to_time%TYPE;
1879 
1880 
1881   CURSOR cur_entities (l_meter_fk_key NUMBER)
1882   IS
1883      SELECT meter_fk_key, entity_fk_key
1884      FROM mth_meter_entities
1885      WHERE meter_fk_key = l_meter_fk_key
1886      AND status = 'ACTIVE';
1887 
1888   CURSOR cur_shifts (l_meter_fk_key NUMBER, l_entity_fk_key NUMBER, l_from_time DATE, l_to_time DATE)
1889   IS
1890      SELECT l_meter_fk_key, l_entity_fk_key, shift_workday_fk_key,
1891             Greatest(from_date, l_from_time) from_time,
1892             least(l_to_time, To_Date) to_time
1893      FROM mth_equipment_shifts_d
1894      WHERE equipment_fk_key = l_entity_fk_key
1895      AND ( from_date <= l_from_time AND to_date >= l_from_time OR
1896            from_date <= l_to_time   AND to_date >= l_to_time OR
1897            from_date >= l_from_time AND from_date <= l_to_time ) AND
1898            from_date IS NOT NULL AND To_Date IS NOT NULL AND
1899            from_date <> To_Date;
1900 
1901 
1902   CURSOR cur_catch_all_shifts (l_meter_fk_key NUMBER, l_entity_fk_key NUMBER, l_from_time DATE, l_to_time DATE)
1903   IS
1904      SELECT l_meter_fk_key, l_entity_fk_key, shift_workday_fk_key,
1905             l_from_time from_time,
1906             l_to_time to_time
1907      FROM mth_equipment_shifts_d
1908      WHERE equipment_fk_key = l_entity_fk_key
1909      AND Trunc(availability_date) = Trunc(l_from_time)
1910      AND Nvl(from_date, Trunc(availability_date)) = Nvl(To_Date, Trunc(availability_date));
1911 
1912   -- Find catch all shifts from workday shifts
1913   CURSOR cur_workday_shifts (l_meter_fk_key NUMBER, l_entity_fk_key NUMBER, l_from_time DATE, l_to_time DATE)
1914   IS
1915      SELECT l_meter_fk_key, l_entity_fk_key, a.shift_workday_pk_key,
1916             l_from_time from_time,
1917             l_to_time to_time
1918      FROM mth_workday_shifts_d a,
1919          (SELECT plant_fk_key site_id, entity_pk_key
1920           FROM mth_equip_entities_mst
1921           UNION ALL
1922           SELECT plant_pk_key site_id, plant_pk_key entity_pk_key
1923           FROM mth_plants_d
1924           UNION ALL
1925           SELECT plant_fk_key site_id, resource_pk_key entity_pk_key
1926           FROM mth_resources_d
1927           UNION ALL
1928           SELECT plant_fk_key site_id, equipment_pk_key entity_pk_key
1929           FROM mth_equipments_d
1930          ) b
1931      WHERE a.plant_fk_key = b.site_id
1932      AND b.entity_pk_key = l_entity_fk_key
1933      AND a.shift_workday_pk_key NOT IN (
1934          SELECT DISTINCT shift_workday_fk_key shift_key
1935          FROM mth_equipment_shifts_d
1936          WHERE equipment_fk_key = l_entity_fk_key)
1937      AND Trunc(a.shift_date) = Trunc(l_from_time)
1938      AND Nvl(a.from_date, Trunc(a.shift_date)) = Nvl(a.To_Date, Trunc(a.shift_date));
1939 
1940      v_new_to_time DATE;
1941 
1942 BEGIN
1943 
1944    p_reading := break_hours;
1945    init_shift_break_table;
1946 
1947    FOR i in p_reading.FIRST..p_reading.LAST LOOP
1948        FOR entity_rec IN cur_entities (p_reading(i).meter_fk_key) LOOP
1949           l_shift_rec_to_time := p_reading(i).from_time;
1950           l_new_from_time := p_reading(i).from_time;
1951           IF p_reading(i).from_time = p_reading(i).to_time THEN
1952              l_shift_rec_to_time := p_reading(i).from_time - 1/(24*60*60);
1953           END IF;
1954           l_to_time := p_reading(i).to_time;
1958           --  by adding one more second when calculating duration  in the formula below.
1955           -- Fixed bug 9965814 - NEED TO REMOVE DATA ROUNDING LOGIC WHEN POPULATING DATA INTO ESA TABLE
1956           --  by removing the ROUND function below.
1957           -- Fixed bug 9965863 - ONE ALLOCATION LOGIC WHEN POPULATING DATA INTO ESA NEEDS ADD ONE SECOND
1959           --  l_duration := REPLACE( Round((p_reading(i).to_time - p_reading(i).from_time)*24*60*60), 0, 1);
1960           l_duration := ((p_reading(i).to_time - p_reading(i).from_time)*24*60*60 + 1);
1961 
1962 
1963           l_entity_fk_key := entity_rec.entity_fk_key;
1964           WHILE l_to_time > l_shift_rec_to_time
1965           LOOP
1966                 BEGIN
1967                     OPEN cur_shifts (p_reading(i).meter_fk_key, l_entity_fk_key, l_new_from_time, l_to_time);
1968                     FETCH cur_shifts INTO p_shift(j).meter_fk_key, p_shift(j).entity_fk_key,
1969                                           p_shift(j).shift_workday_fk_key, p_shift(j).from_time, p_shift(j).to_time;
1970 
1971                     IF cur_shifts%NOTFOUND OR l_new_from_time < p_shift(j).from_time THEN
1972                        v_new_to_time :=  CASE WHEN cur_shifts%NOTFOUND
1973                                                    THEN l_to_time
1974                                                    ELSE p_shift(j).from_time - 1/86400
1975                                               END;
1976                        OPEN cur_catch_all_shifts (p_reading(i).meter_fk_key,
1977                                                   l_entity_fk_key,
1978                                                   l_new_from_time,
1979                                                   v_new_to_time);
1980                        FETCH cur_catch_all_shifts INTO p_shift(j).meter_fk_key, p_shift(j).entity_fk_key,
1981                                                        p_shift(j).shift_workday_fk_key,
1982                                                        p_shift(j).from_time, p_shift(j).to_time;
1983 
1984                        IF cur_catch_all_shifts%NOTFOUND THEN
1985                           OPEN cur_workday_shifts (p_reading(i).meter_fk_key,
1986                                                    l_entity_fk_key,
1987                                                    l_new_from_time,
1988                                                    v_new_to_time);
1989                           FETCH cur_workday_shifts INTO p_shift(j).meter_fk_key, p_shift(j).entity_fk_key,
1990                                                         p_shift(j).shift_workday_fk_key,
1991                                                         p_shift(j).from_time, p_shift(j).to_time;
1992 
1993                           IF cur_workday_shifts%NOTFOUND THEN
1994                              p_shift(j).meter_fk_key := p_reading(i).meter_fk_key;
1995                              p_shift(j).entity_fk_key := l_entity_fk_key;
1996                              p_shift(j).shift_workday_fk_key := -99999;
1997                              p_shift(j).from_time := l_new_from_time;
1998                              p_shift(j).to_time := v_new_to_time;
1999                           END IF;
2000                           CLOSE cur_workday_shifts;
2001                        END IF;
2002                        CLOSE cur_catch_all_shifts;
2003                     END IF;
2004                  EXCEPTION
2005                     WHEN OTHERS THEN
2006                         raise_application_error(-20002,'Unknown Exception to allocate meter reading');
2007                  END;
2008                  CLOSE cur_shifts;
2009                  p_shift(j).hour_fk_key := p_reading(i).hour_pk_key;
2010                  -- Fixed bug 9965814 - NEED TO REMOVE DATA ROUNDING LOGIC WHEN POPULATING DATA INTO ESA TABLE
2011                  --  by removing the ROUND function below.
2012                  -- Fixed bug 9965863 - ONE ALLOCATION LOGIC WHEN POPULATING DATA INTO ESA NEEDS ADD ONE SECOND
2013                  --  by adding one more second when calculating duration  in the formula below.
2014                  --   p_shift(j).usage_value := Round(REPLACE(Round((p_shift(j).to_time - p_shift(j).from_time)*24*60*60),0,1)/l_duration*p_reading(i).usage_value,2);
2015                  p_shift(j).usage_value := ((p_shift(j).to_time - p_shift(j).from_time)*24*60*60 + 1)/l_duration*p_reading(i).usage_value;
2016                  l_shift_rec_to_time := p_shift(j).to_time;
2017                  l_new_from_time := p_shift(j).to_time + 1/(24*60*60);
2018                  j := j + 1;
2019                  p_shift.extend(1);
2020             END LOOP;
2021        END LOOP;
2022    END LOOP;
2023 
2024 EXCEPTION
2025    WHEN OTHERS THEN
2026       ROLLBACK;
2027       raise_application_error(-20003,'Error occurs when break shifts:'||SQLCODE||' -ERROR- '||SQLERRM);
2028 
2029 END break_reading;
2030 
2031 
2032 PROCEDURE load_sustain_emission_to_hour
2033 IS
2034 
2035 BEGIN
2036 
2037    INSERT INTO MTH_ENTITY_SUST_HR_EMISSIONS
2038              (esa_hr_fk_key, emission_code, emission_name, emission_quantity,
2039               emission_uom)
2040        SELECT Max(esa_pk_key) AS esa_hr_fk_key,
2041               e.emission_code,
2042               e.emission_name,
2043               sum(e.emission_quantity) AS emission_quantity,
2044               e.emission_uom
2045        FROM mth_entity_sustain_aspect a, mth_hour_d h,
2046             MTH_ENTITY_SUST_EMISSIONS e
2047        WHERE a.hour_fk_key = h.hour_pk_key AND e.esa_fk_key = a.esa_pk_key
2048        GROUP BY a.plant_fk_key, a.entity_fk_key, a.entity_type,
2049               a.sustain_aspect, a.usage_category, a.meter_fk_key,
2050               a.meter_category, h.from_time, h.to_time, a.hour_fk_key,
2051               a.shift_workday_fk_key, a.entity_name, a.entity_type_name,
2052               a.sustain_aspect_name, a.usage_category_name, a.meter_type,
2053               a.meter_type_name, a.meter_category_name,
2054               a.simulation_name, a.usage_uom, a.currency,
2055               e.emission_code, e.emission_name, e.emission_uom
2059               a.entity_name, a.entity_type_name,
2056        order BY a.entity_fk_key, a.entity_type, a.sustain_aspect,
2057               a.usage_category, a.meter_fk_key, a.meter_category,
2058               h.from_time, h.to_time, a.hour_fk_key,a.shift_workday_fk_key,
2060               a.sustain_aspect_name, a.usage_category_name, a.meter_type,
2061               a.meter_type_name, a.meter_category_name,
2062               a.simulation_name, a.usage_uom, a.currency,
2063               e.emission_code, e.emission_name, e.emission_uom;
2064 
2065 EXCEPTION
2066    WHEN OTHERS THEN
2067         ROLLBACK;
2068         raise_application_error(-20005,
2069           'Error occurs when inserting MTH_ENTITY_SUST_HR_EMISSIONS table:'
2070           ||SQLCODE||' -ERROR- '||SQLERRM);
2071 END load_sustain_emission_to_hour;
2072 
2073 
2074 PROCEDURE load_sustain_aspect_to_hour
2075 IS
2076 
2077 BEGIN
2078 
2079    DELETE FROM MTH_ENTITY_SUST_HR_EMISSIONS;
2080    DELETE FROM mth_entity_sustain_aspect_hour;
2081 
2082    INSERT INTO mth_entity_sustain_aspect_hour
2083              (esa_hr_pk_key, plant_fk_key, entity_fk_key, entity_type,
2084               sustain_aspect_fk_key,
2085               sustain_aspect, usage_category, meter_fk_key, meter_category,
2086               from_time, to_time, hour_fk_key, shift_workday_fk_key,
2087               entity_name, entity_type_name,
2088               sustain_aspect_name, usage_category_name, meter_type,
2089               meter_type_name, meter_category_name,
2090               simulation_name, usage_value, usage_uom, usage_cost,
2091               currency, creation_date, last_update_date)
2092        SELECT max(esa_pk_key) AS esa_hr_pk_key,
2093               a.plant_fk_key, a.entity_fk_key, a.entity_type,
2094               a.sustain_aspect_fk_key,
2095               a.sustain_aspect, a.usage_category, a.meter_fk_key,
2096               a.meter_category, h.from_time, h.to_time, a.hour_fk_key,
2097               a.shift_workday_fk_key, a.entity_name, a.entity_type_name,
2098               a.sustain_aspect_name, a.usage_category_name, a.meter_type,
2099               a.meter_type_name, a.meter_category_name,
2100               a.simulation_name, Sum(a.usage_value) AS usage_value,
2101               a.usage_uom, Sum(a.usage_cost) AS usage_cost,
2102               a.currency, SYSDATE AS creation_date,
2103               SYSDATE AS last_update_date
2104        FROM mth_entity_sustain_aspect a, mth_hour_d h
2105        WHERE a.hour_fk_key = h.hour_pk_key
2106        GROUP BY a.plant_fk_key, a.entity_fk_key, a.entity_type,
2107               a.sustain_aspect, a.sustain_aspect_fk_key, a.usage_category, a.meter_fk_key,
2108               a.meter_category, h.from_time, h.to_time, a.hour_fk_key,
2109               a.shift_workday_fk_key, a.entity_name, a.entity_type_name,
2110               a.sustain_aspect_name, a.usage_category_name, a.meter_type,
2111               a.meter_type_name, a.meter_category_name,
2112               a.simulation_name, a.usage_uom, a.currency
2113        order BY a.entity_fk_key, a.entity_type, a.sustain_aspect,
2114               a.sustain_aspect_fk_key,
2115               a.usage_category, a.meter_fk_key, a.meter_category,
2116               h.from_time, h.to_time, a.hour_fk_key,a.shift_workday_fk_key,
2117               a.entity_name, a.entity_type_name,
2118               a.sustain_aspect_name, a.usage_category_name, a.meter_type,
2119               a.meter_type_name, a.meter_category_name,
2120               a.simulation_name, a.usage_uom, a.currency;
2121 
2122   load_sustain_emission_to_hour;
2123 
2124 EXCEPTION
2125    WHEN OTHERS THEN
2126         ROLLBACK;
2127         raise_application_error(-20005,
2128           'Error occurs when inserting MTH_ENTITY_SUSTAIN_ASPECT_HOUR table:'
2129           ||SQLCODE||' -ERROR- '||SQLERRM);
2130 END load_sustain_aspect_to_hour;
2131 
2132 
2133 
2134 /* ****************************************************************************
2135 * Procedure   :load_data_to_sustain_emissions                                 *
2136 * Description :Load data into the child table MTH_ENTITY_SUST_EMISSIONS       *
2137 * File Name             :MTHSUSAB.PLS                                         *
2138 * Visibility            :Private                                              *
2139 * Parameters            :None                                                 *
2140 * Return Value          :None                                                 *
2141 **************************************************************************** */
2142 
2143 PROCEDURE load_data_to_sustain_emissions
2144 AS
2145 
2146 l_start_time DATE;
2147 l_end_time DATE;
2148 l_count NUMBER;
2149 
2150 BEGIN
2151 
2152    FOR i IN p_shift.FIRST..p_shift.LAST LOOP
2153        INSERT INTO MTH_ENTITY_SUST_EMISSIONS
2154              (esa_fk_key, emission_code, emission_name, emission_quantity,
2155               emission_uom)
2156       SELECT p_shift(i).esa_pk_key,
2157               y.emission_code,
2158               (SELECT meaning FROM fnd_lookup_values_vl
2159                WHERE lookup_type = 'MTH_SUSTAIN_EMISSION'
2160                AND lookup_code = y.emission_code) emission_name,
2161                p_shift(i).usage_value *
2162                   y.average_emission_factor AS emission_quantity,
2163               (SELECT emission_uom FROM mth_sustain_emissions
2164                WHERE sustain_aspect_fk_key = w.sustain_aspect_pk_key
2165                AND emission_code = y.emission_code) emission_uom
2166           FROM MTH_METER_ENTITIES x, mth_meters x1,
2167                (SELECT m.plant_fk_key, m.effective_date, m.expiration_date, m.sustain_aspect_fk_key,
2168                        m.average_planned_cost, n.emission_code, Nvl(n.average_emission_factor,0) average_emission_factor
2169                 FROM MTH_SITE_SUSTAINABILITIES m,
2170                      (SELECT a.site_sustain_pk_key, c.sustain_emission_fk_key, d.emission_code,
2174                       WHERE a.site_sustain_pk_key = b.site_sustain_fk_key
2171                              sum(b.planned_usage_percentage/100 * Nvl(c.emission_factor,0)) average_emission_factor
2172                       FROM MTH_SITE_SUSTAINABILITIES a, MTH_SITE_SUSTAIN_SOURCES b,
2173                            MTH_SOURCE_EMISSION_FACTORS c, MTH_SUSTAIN_EMISSIONS d
2175                       AND b.site_sustain_source_pk_key = c.site_sustain_source_fk_key
2176                       AND d.sustain_emission_pk_key = c.sustain_emission_fk_key
2177                       AND a.sustain_aspect_fk_key = d.sustain_aspect_fk_key
2178                       AND d.status = 'ACTIVE'
2179                       AND  p_shift(i).to_time BETWEEN a.effective_date AND Nvl(a.expiration_date, SYSDATE)
2180                       GROUP by a.site_sustain_pk_key, c.sustain_emission_fk_key, d.emission_code
2181                      ) n
2182                 WHERE m.site_sustain_pk_key = n.site_sustain_pk_key
2183                 AND  p_shift(i).to_time BETWEEN m.effective_date AND Nvl(m.expiration_date, SYSDATE)
2184                ) y,
2185                (SELECT f.*, g.meaning entity_type_name
2186                 FROM mth_all_entities_v f, fnd_lookup_values_vl g
2187                 WHERE Upper(f.entity_type) = g.lookup_code
2188                 AND (g.lookup_type = 'MTH_USER_DEFINED_ENTITIES' OR
2189                      g.lookup_type = 'MTH_OTHER_ENTITY_TYPE')
2190                ) z,
2191                (SELECT f.sustain_aspect_pk_key, f.sustain_aspect_code,
2192                        f.usage_uom, g.meaning
2193                 FROM mth_sustain_aspects f, fnd_lookup_values_vl g
2194                 WHERE f.sustain_aspect_code = g.lookup_code
2195                 AND g.lookup_type = 'MTH_SUSTAIN_ASPECT'
2196                ) w
2197           WHERE x.STATUS = 'ACTIVE'
2198           AND x.meter_fk_key = x1.meter_pk_key
2199           AND x.entity_fk_key = z.entity_pk_key
2200           AND x.entity_type = Upper(z.entity_type)
2201           AND y.plant_fk_key = z.site_id
2202           AND w.sustain_aspect_pk_key = y.sustain_aspect_fk_key
2203           AND w.sustain_aspect_pk_key = x1.sustain_aspect_fk_key
2204           AND x.meter_fk_key = p_shift(i).meter_fk_key
2205           AND x.entity_fk_key = p_shift(i).entity_fk_key;
2206 END LOOP;
2207 EXCEPTION
2208    WHEN OTHERS THEN
2209         ROLLBACK;
2210         raise_application_error(-20004,
2211         'Error occurs when inserting data into MTH_ENTITY_SUST_EMISSIONS table:'
2212          || SQLCODE||' -ERROR- '||SQLERRM);
2213 
2214 END load_data_to_sustain_emissions;
2215 
2216 
2217 
2218 
2219 PROCEDURE load_reading_to_sustain_aspect
2220 AS
2221 
2222 l_start_time DATE;
2223 l_end_time DATE;
2224 l_count NUMBER;
2225 
2226 BEGIN
2227    init_hour_reading_table;
2228    init_shift_break_table;
2229    break_reading;
2230 
2231    -- Fill the esa_pk_key
2232    FOR i IN p_shift.FIRST..p_shift.LAST LOOP
2233      SELECT  MTH_ENTITY_SUSTAIN_ASPECT_S.NEXTVAL
2234      INTO    p_shift(i).esa_pk_key
2235      FROM    dual;
2236    END LOOP;
2237 
2238 
2239    FOR i IN p_shift.FIRST..p_shift.LAST LOOP
2240        INSERT INTO mth_entity_sustain_aspect
2241              (esa_pk_key, plant_fk_key, entity_fk_key, entity_type,
2242               sustain_aspect_fk_key,
2243               sustain_aspect, usage_category, meter_fk_key, meter_category,
2244               from_time, to_time, hour_fk_key, shift_workday_fk_key,
2245               entity_name, entity_type_name,
2246               sustain_aspect_name, usage_category_name, meter_type,
2247               meter_type_name, meter_category_name,
2248               simulation_name, usage_value, usage_uom, usage_cost,
2249                currency, creation_date, last_update_date)
2250       SELECT  p_shift(i).esa_pk_key, y.plant_fk_key, x.entity_fk_key,
2251               z.entity_type, w.sustain_aspect_pk_key, w.sustain_aspect_code,
2252               Nvl(x.usage_category_code, -99999), x.meter_fk_key,
2253               Nvl(x.meter_category_code, -99999),
2254               p_shift(i).from_time, p_shift(i).to_time,
2255               p_shift(i).hour_fk_key, p_shift(i).shift_workday_fk_key,
2256               z.entity_name, z.entity_type_name,
2257               w.meaning, (SELECT meaning FROM fnd_lookup_values_vl
2258                           WHERE lookup_type = 'MTH_ENERGY_USAGE_CATEGORIES'
2259                           AND lookup_code = x.usage_category_code) usage_category_name,
2260               x1.meter_type, (SELECT meaning FROM fnd_lookup_values_vl
2261                               WHERE lookup_type = 'MTH_METER_TYPE'
2262                               AND lookup_code = x1.meter_type) meter_type_name,
2263               (SELECT meaning FROM fnd_lookup_values_vl
2264                WHERE lookup_type = 'MTH_METER_CATEGORY'
2265                AND lookup_code = x.meter_category_code) meter_category_name,
2266               (SELECT meaning FROM fnd_lookup_values_vl
2267                WHERE lookup_type = 'MTH_SIMULATION_NAME'
2268                AND lookup_code = x.simulation_name_code) simulation_name,
2269               p_shift(i).usage_value,
2270               w.usage_uom,
2271               p_shift(i).usage_value * y.average_planned_cost,
2272               (SELECT i.currency_code FROM mth_plants_d i
2273                WHERE i.plant_pk_key = z.site_id) currency,
2274               SYSDATE, SYSDATE
2275           FROM MTH_METER_ENTITIES x, mth_meters x1,
2276                (SELECT m.plant_fk_key, m.effective_date, m.expiration_date, m.sustain_aspect_fk_key,
2277                        m.average_planned_cost
2278                 FROM MTH_SITE_SUSTAINABILITIES m
2279                 WHERE  p_shift(i).to_time BETWEEN m.effective_date AND Nvl(m.expiration_date, SYSDATE)
2280                ) y,
2281                (SELECT f.*, g.meaning entity_type_name
2282                 FROM mth_all_entities_v f, fnd_lookup_values_vl g
2283                 WHERE Upper(f.entity_type) = g.lookup_code
2287                (SELECT f.sustain_aspect_pk_key, f.sustain_aspect_code,
2284                 AND (g.lookup_type = 'MTH_USER_DEFINED_ENTITIES' OR
2285                      g.lookup_type = 'MTH_OTHER_ENTITY_TYPE')
2286                ) z,
2288                        f.usage_uom, g.meaning
2289                 FROM mth_sustain_aspects f, fnd_lookup_values_vl g
2290                 WHERE f.sustain_aspect_code = g.lookup_code
2291                 AND g.lookup_type = 'MTH_SUSTAIN_ASPECT'
2292                ) w
2293           WHERE x.STATUS = 'ACTIVE'
2294           AND x.meter_fk_key = x1.meter_pk_key
2295           AND x.entity_fk_key = z.entity_pk_key
2296           AND x.entity_type = Upper(z.entity_type)
2297           AND y.plant_fk_key = z.site_id
2298           AND w.sustain_aspect_pk_key = y.sustain_aspect_fk_key
2299           AND w.sustain_aspect_pk_key = x1.sustain_aspect_fk_key
2300           AND x.meter_fk_key = p_shift(i).meter_fk_key
2301           AND x.entity_fk_key = p_shift(i).entity_fk_key;
2302 end loop;
2303 
2304    -- Load emission data into MTH_ENTITY_SUST_EMISSIONS child table
2305    load_data_to_sustain_emissions;
2306 
2307    load_sustain_aspect_to_hour;
2308 
2309 EXCEPTION
2310    WHEN OTHERS THEN
2311         ROLLBACK;
2312         raise_application_error(-20004,
2313         'Error occurs when inserting MTH_ENTITY_SUSTAIN_ASPECT table:'
2314         ||SQLCODE||' -ERROR- '||SQLERRM);
2315 
2316 END load_reading_to_sustain_aspect;
2317 
2318 
2319 
2320 /* ****************************************************************************
2321 * Procedure :truncate_entity_sustain_data                                     *
2322 * Description 	 	:Truncate sustainability aspect tables and set the          *
2323 *                   production performance table to be null.                  *
2324 * File Name             :MTHSUSAB.PLS                                         *
2325 * Visibility            :Private                                              *
2326 * Parameters            :None                                                 *
2327 * Return Value          :None                                                 *
2328 **************************************************************************** */
2329 PROCEDURE truncate_entity_sustain_data
2330 IS
2331 BEGIN
2332   mth_util_pkg.mth_truncate_table('MTH_TAG_METER_READINGS_LATEST');
2333   mth_util_pkg.mth_truncate_table('MTH_METER_READINGS');
2334   mth_util_pkg.mth_truncate_table('MTH_METER_READINGS_ERR');
2335   mth_util_pkg.mth_truncate_table('MTH_ENTITY_SUST_EMISSIONS');
2336   mth_util_pkg.mth_truncate_table('MTH_ENTITY_SUSTAIN_ASPECT');
2337   mth_util_pkg.mth_truncate_table('MTH_ENTITY_SUST_HR_EMISSIONS');
2338   mth_util_pkg.mth_truncate_table('MTH_ENTITY_SUSTAIN_ASPECT_HOUR');
2339   mth_util_pkg.mth_truncate_table('MTH_EQUIP_PROD_SUSTAIN_F');
2340 
2341 END truncate_entity_sustain_data;
2342 
2343 PROCEDURE process_entity_sustain_aspect
2344   (p_err_buff                     out NOCOPY  VARCHAR2,
2345    p_retcode                      out NOCOPY NUMBER,
2346    p_process_mode                 IN VARCHAR2 DEFAULT 'INCR',
2347    p_virtual_meter_start_date_str IN varchar2 )
2348 IS
2349   v_raw_tab_name VARCHAR2(30) := 'MTH_TAG_METER_READINGS_RAW';
2350   v_curr_partition NUMBER;
2351   v_virtual_meter_start_date date;
2352   v_process_mode VARCHAR2(200);
2353 BEGIN
2354 
2355   v_process_mode := p_process_mode;
2356   IF (v_process_mode IS NULL OR length(v_process_mode) = 0) THEN
2357     v_process_mode := 'INCR';
2358   END IF;
2359 
2360   IF (v_process_mode = 'INIT') THEN
2361     truncate_entity_sustain_data;
2362   END IF;
2363 
2364   IF (v_process_mode = 'INCR' OR v_process_mode = 'INIT') THEN
2365 
2366     v_virtual_meter_start_date :=
2367       NVL(fnd_date.canonical_to_date(p_virtual_meter_start_date_str),trunc(sysdate));
2368 
2369 
2370     mth_util_pkg.switch_column_default_value(v_raw_tab_name, v_curr_partition);
2371 
2372     IF (v_curr_partition = 1 OR v_curr_partition = 2) THEN
2373       LOAD_ACT_METER_RAW_TO_READINGS( v_curr_partition );
2374 
2375       mth_util_pkg.truncate_table_partition (v_raw_tab_name, v_curr_partition);
2376     END IF;
2377 
2378 
2379     ADD_VRT_MTS_TO_METER_READINGS (v_virtual_meter_start_date);
2380 
2381     COMMIT;
2382     load_reading_to_sustain_aspect;
2383     COMMIT;
2384 
2385     fnd_file.put_line(FND_FILE.LOG,
2386     'Processing and populating data into entity sustainability aspect completed successfully');
2387     p_err_buff :=
2388     'Processing and populating data into entity sustainability aspect completed successfully';
2389     p_retcode := 0;
2390   ELSE
2391     p_retcode := 1;
2392     fnd_file.put_line(FND_FILE.LOG,
2393     'The process mode can only be INIT or INCR for initial and incremental respectively.'
2394               );
2395     fnd_file.put_line(FND_FILE.LOG,-20005);
2396     p_err_buff :=
2397      'The process mode can only be INIT or INCR for initial and incremental respectively.';
2398   END IF;
2399 
2400     EXCEPTION
2401        WHEN OTHERS THEN
2402             ROLLBACK;
2403             p_retcode := 1;
2404             fnd_file.put_line(FND_FILE.LOG,substr(sqlerrm,1,300));
2405             fnd_file.put_line(FND_FILE.LOG,sqlcode);
2406             p_err_buff := substr(sqlerrm,1,240);
2407             --RAISE;
2408 
2409 
2410 
2411 END process_entity_sustain_aspect;
2412 
2413 END MTH_SUSTAIN_ASPECT_PKG;