#!/bin/sh  
# \
exec oagwish "$0" "$@"


set auto_path [linsert $auto_path 0 /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSDebugPath

set CVSRevisionAuthor "\$Revision: 1.6 $ \$Author: shang $"

APSApplication . -name BeamlineDownTimeCalculator -version $CVSRevisionAuthor \
      -overview {This application (1) computes the total time during any period during which the beamline was unavailable. In addition, (2)compute the total time the beamline was unavailable when beam was available from the storage ring.}

proc SetStatus {text args} {
    global status
    set status "[clock format [clock seconds] -format %H:%M:%S] $text"
    update      
}

proc GetBeamlineList {args} {
    global sectorList missingList logDirectory ControlNameIndex
    
    set names [exec sdds2stream $logDirectory/EPS.loc -col=ControlName]
    for {set i 0} {$i<[llength $names]} {incr i} {
        set ControlNameIndex([lindex $names $i]) $i
    }
    if [catch {exec sddsprocess $logDirectory/EPS.loc -pipe=out \
                 -match=col,ControlName=*:ID:BLEPS:SPER \
                 -scan=col,Beamline,ControlName,EPS:%ld,type=long \
                 | sddssort -pipe -col=Beamline \
                 | sdds2stream -pipe -col=Beamline } IDBeamlineList] {
        return -code error $IDBeamlineList
    }
    if [catch {exec sddsprocess $logDirectory/EPS.loc -pipe=out \
                 -match=col,ControlName=*:BM:BLEPS:SPER \
                 -scan=col,Beamline,ControlName,EPS:%ld,type=long \
                 | sddssort -pipe -col=Beamline \
                 | sdds2stream -pipe -col=Beamline } BMBeamlineList] {
        return -code error "$BMBeamlineList"
    }
    set sectorList [lsort -unique -integer [concat $IDBeamlineList $BMBeamlineList]]
    set IDMissingList ""
    set BMMissingList ""
    set index 1
    foreach sector $sectorList {
        if {[lsearch -exact $IDBeamlineList $sector]<0} {
            lappend IDMissingList S${index}ID
        }
        if {[lsearch -exact $BMBeamlineList $sector]<0} {
            lappend BMMissingList S${index}BM
        }
        incr index
    }
    set missingList [concat $IDMissingList $BMMissingList]
}

proc SetDateTimeToToday {args} {
    set hour 0
    set rootname ""
    APSStrictParseArguments {hour rootname}
    global todayMonth todayYear todayDay 
    global ${rootname}Month ${rootname}Year ${rootname}Day ${rootname}Hour
    
    APSDateBreakDown -dayVariable todayDay -yearVariable todayYear \
      -monthVariable todayMonth -twoDigitYear 0 -leadingZeros 0
    set ${rootname}Hour $hour
    set ${rootname}Month $todayMonth
    set ${rootname}Day $todayDay
    set ${rootname}Year $todayYear
}

proc MakeDateTimeFrame {widget args} {
    set parent .
    APSStrictParseArguments {parent}
    set label "Date/Time Range of Interest"
   
    APSFrame $widget -parent $parent -label $label
    set w $parent$widget.frame

    
    APSDateTimeAdjEntry .startDate -parent $w \
      -yearVariable StartYear \
      -monthVariable StartMonth \
      -dayVariable StartDay \
      -hourVariable StartHour \
      -label "Starting date/time (year, month, day, hour): " -defaultHour 0 
    APSDateTimeAdjEntry .endDate -parent $w \
      -yearVariable EndYear \
      -monthVariable EndMonth \
      -dayVariable EndDay \
      -hourVariable EndHour \
      -label "Ending date/time (year, month, day, hour):   " -defaultHour 24 
    
    SetDateTimeToToday -rootname Start -hour 0
    SetDateTimeToToday -rootname End  -hour 24
}

proc CalculateDownTime {args} {
    global logDirectory ControlNameIndex SelectedBeamlineList SelectedBPMList
    global StartYear StartDay StartMonth EndYear EndDay EndMonth StartHour EndHour
    global StartTime EndTime BeamAvailableTime withPlots
    if [catch {GetSelectedBPMList} filterOptions] {
        SetStatus "$filterOptions"
        return
    }
    
    SetStatus "Calculating...."
    set files [APSFindFilesBetweenDates \
                 -rootname "EPS-" \
                 -directory $logDirectory \
                 -startDateList [APSFormatDate \
                                   -year $StartYear \
                                   -day $StartDay \
                                   -month $StartMonth \
                                   -dateFormat list] \
                 -endDateList [APSFormatDate \
                                 -year $EndYear \
                                 -day $EndDay \
                                 -month $EndMonth \
                                 -dateFormat list]]
    set StartTime [expr [clock scan "[format %.2d $StartMonth]/[format %.2d $StartDay]/$StartYear"] + $StartHour * 3600]
    set EndTime [expr  [clock scan "[format %.2d $EndMonth]/[format %.2d $EndDay]/$EndYear"] + $EndHour * 3600]
    set currentTime [clock seconds]
    if {$EndTime<$StartTime} {
        SetStatus "Invalid time range: the end time is smaller than the start time!"
        return
    }
    set StartTimeStamp "$StartYear-$StartMonth-$StartDay $StartHour Hour"
    set EndTimeStamp "$EndYear-$EndMonth-$EndDay $EndHour Hour"
   
    if {![llength $files]} {
        SetStatus "No files found in the time range."
        return
    }
    if [catch {CalculateBeamAvailableTime} result] {
        SetStatus "Can not get the beam available time: $result"
        return
    }
    set beamStatusFile /tmp/SRBeamStatus-$StartYear-$StartMonth$StartDay:$StartHour.$EndYear-$EndMonth$EndDay:$EndHour
  
    set tmpfile /tmp/[APSTmpString]
    set tmpfileList ""
    set index 0
    foreach file $files {
        set pages [exec sdds2stream $file -npages=bare]
        if {$pages>1} {
            set i 1
            while {$i<=$pages} {
                APSAddToTmpFileList -ID downtime -fileList "$tmpfile.proc.$i"
                if [catch {exec sddsconvert $file -fromPage=$i -toPage=$i $tmpfile.proc.$i 
                    exec sddsconvertlogonchange $tmpfile.proc.$i -pipe=out  \
                             | sddsconvert -pipe=in $tmpfile.$index.proc $filterOptions } result] {
                    return -code error $result
                }
                lappend tmpfileList $tmpfile.$index.proc
                incr index
                incr i
            }
        } elseif {$pages==1} {
            if [catch {exec sddsconvertlogonchange $file -pipe=out \
                         | sddsconvert -pipe=in $tmpfile.$index.proc $filterOptions } result] {
                return -code error $result
            }
            lappend tmpfileList $tmpfile.$index.proc
            incr index
        }
    }
    APSAddToTmpFileList -ID downtime -fileList $tmpfileList
    if [catch {eval exec sddscombine $tmpfileList -merge -overwrite $tmpfile.all } result] {
        SetStatus "$result"
        return
    }
    APSAddToTmpFileList -ID downtime -fileList "$tmpfile $tmpfile.1"
    set fileList ""
    set downTimeList ""
    set downTimeWithScheduleList ""
    set downTimeWithDeliveredBeamList ""
    set totalTime [expr $EndTime - $StartTime]
    foreach bpm $SelectedBPMList {
        set index $ControlNameIndex($bpm)
        SetStatus "Processing $bpm (index=$index) ..."
        if [catch {exec sddsconvert $tmpfile.all -pipe=out -retain=col,Time,$bpm  \
                     -rename=col,$bpm=Value \
                     | sddssort -pipe=in $tmpfile.$bpm -col=Time -unique } result] {
            SetStatus "$result"
            return
        }
        APSAddToTmpFileList -ID downtime\
          -fileList "$tmpfile.$bpm $tmpfile.$bpm.1 $tmpfile.$bpm.2 $tmpfile.$bpm.3"
        
        #insert new rows to the original file to make proper function 
        if [catch {exec sddsbreak $tmpfile.$bpm -changeof=Value -pipe=out \
                     | sddsprocess -pipe -clip=1,0,invert \
                     "-define=col,InsertTime,Time 0.1 -" \
                     "-define=col,InsertValue,1.0 Value -" \
                     | sddsconvert -pipe -del=col,Time,Value \
                     -rename=col,InsertTime=Time,InsertValue=Value \
                     | sddscombine -pipe -merge \
                     | sddssort -pipe=in -col=Time $tmpfile.$bpm.1 } result] {
            SetStatus "$result"
            return
        }
        #remove the first added line (was added line)
        if [catch {exec sddscombine $tmpfile.$bpm $tmpfile.$bpm.1 -merge -pipe=out \
                     | sddssort -pipe -col=Time \
                     | sddsprocess -pipe=in $tmpfile.$bpm.2 \
                     -clip=1,0 -nowarnings  } result] {
            SetStatus "$result"
            return
        }
        if [catch {exec sddsinterp $tmpfile.$bpm.2 $tmpfile.$bpm.3 -atValues=$StartTime,$EndTime \
                     -col=Time,Value -belowRange=saturate -aboveRange=saturate
            exec sddscombine $tmpfile.$bpm.2 $tmpfile.$bpm.3 -merge -pipe=out \
                     | sddssort -pipe -col=Time \
                     | sddsprocess -pipe=in /tmp/$bpm -filter=col,Time,$StartTime,$EndTime \
                     "-define=col,Value1,Value 0 == pop pop ? 1 : 0 $ " \
                 } result] {
            SetStatus "$result"
            return
        }
        APSAddToTmpFileList -ID downtime -fileList "$tmpfile.$bpm.2 $tmpfile.$bpm.3"
        
        if [catch {CalculateDownTimeWhenBeamAvailable -beamStatusFile $beamStatusFile \
                     -beamlineFile /tmp/$bpm } downTimes] {
            SetStatus "$downTimes"
            return
        }
        lappend downTimeList [lindex $downTimes 0]
        lappend downTimeWithScheduleList [lindex $downTimes 1]
        lappend downTImeWithDeliveredBeamList [lindex $downTimes 2]
        lappend fileList /tmp/$bpm
        SetStatus "The total down time of $bpm is [lindex $downTimes 0]"
        SetStatus "The down time with scheduled user operation of $bpm is [lindex $downTimes 1]"
        SetStatus "The down time with delivered beam of $bpm is [lindex $downTimes 2]"
    }
    
    if [catch {exec sddsmakedataset /tmp/$bpm.proc -defaultType=double \
                 -col=Beamline,type=string -data=[join $SelectedBeamlineList ,] \
                 -col=ControlName,type=string -data=[join $SelectedBPMList ,] \
                 -col=TotalDownTime,units=Hour -data=[join $downTimeList ,] \
                 -col=DownTimeWithScheduledUserOper,units=Hour -data=[join $downTimeWithScheduleList ,] \
                 -col=DownTimeWithDeliveredBeam,units=Hour -data=[join $downTImeWithDeliveredBeamList ,] \
                 -par=TotalTime,units=Hour -data=[expr $totalTime /3600.0] \
                 -par=StarTimeStamp,type=string "-data=$StartTimeStamp" \
                 -par=EndTimeStamp,type=string "-data=$EndTimeStamp" 
        exec sddsprintout /tmp/$bpm.proc $tmpfile.printout \
                 "-title=Down time in hours for selected beamlines from $StartTimeStamp to $EndTimeStamp\n" \
                 -col=Beamline,format=%10s "-col=TotalDownTime,format=%20.3f" \
                 "-col=DownTimeWithScheduledUserOper,format=%20.3f" \
                 "-col=DownTimeWithDeliveredBeam,format=%.3f" } result] {
        SetStatus "$result"
        return
    }
    APSFileDisplayWindow .[APSUniqueName printout] -fileName $tmpfile.printout \
      -width 100 -printCommand "enscript -r" \
      -deleteOnClose 1 -height 30 
    
    APSAddToTmpFileList -ID downtime -fileList $fileList
    if {$withPlots} {
        eval exec sddsplot $fileList -graph=line,vary -ticks=xtime -legend=rootname,edit=5d -col=Time,Value &
    } else {
        APSDeleteTmpFileList -ID downtime
    }
    SetStatus "done."
}


proc GetSelectedBPMList {args} {
    global BeamlineArray SelectedBeamlineList SelectedBPMList ControlNameIndex
    
    set SelectedBPMList ""
    set filterOptions ""
    set SelectedBeamlineList ""
    set firstmatch 1
    foreach name [array name BeamlineArray] {
       
        global $BeamlineArray($name)
        if [set $BeamlineArray($name)] {
            set sector [scan $name %2d]
            set bpm ""
            if [regexp {BM} $name a b] {
                set bpm EPS:[format %.2d $sector]:BM:BLEPS:SPER
            } elseif [regexp {ID} $name a b] {
                set bpm EPS:[format %.2d $sector]:ID:BLEPS:SPER
            }
            if [string length $bpm] {
                lappend SelectedBPMList $bpm
                lappend SelectedBeamlineList $name
                if {$firstmatch} {
                    set filterOptions "-retain=col,Time,$bpm"
                } else {
                    append filterOptions ",$bpm"
                }
                set firstmatch 0
            }
        }
    }
    if ![llength $SelectedBPMList] {
        return -code error "No beamline is selected!"
    }
   
    return $filterOptions
}

proc CalculateBeamAvailableTime {args} {
    global StartYear StartDay StartMonth EndYear EndDay EndMonth StartTime EndTime StartHour EndHour
    global BeamAvailableTime withPlots

    if [file exists /tmp/SRBeamStatus-$StartYear-$StartMonth$StartDay:$StartHour.$EndYear-$EndMonth$EndDay:$EndHour ] {
        return
    }
    APSAddToTmpFileList -ID downtimeStatus -fileList "/tmp/SRBeamStatus-$StartYear-$StartMonth$StartDay:$StartHour.$EndYear-$EndMonth$EndDay:$EndHour"
    set directory /home/helios/oagData/monitoring/Charge
    
    #SetStatus "Calculating Beam available time ..."
    set files [APSFindFilesBetweenDates \
                 -rootname "ChargeFast-" \
                 -directory $directory \
                 -startDateList [APSFormatDate \
                                   -year $StartYear \
                                   -day $StartDay \
                                   -month $StartMonth \
                                   -dateFormat list] \
                 -endDateList [APSFormatDate \
                                 -year $EndYear \
                                 -day $EndDay \
                                 -month $EndMonth \
                                 -dateFormat list]]
    if {![llength $files]} {
        return -code error "No files found in the time range!"
    }
    SetStatus "[llength $files] files found for beam current logger"
    set tmpfile /tmp/[APSTmpString]
    APSAddToTmpFileList -ID downtime -fileList "$tmpfile"
    set beamAvailableTime 0.0
    set index 1
    set fileList ""
    foreach file $files {
        SetStatus "Processing file $index ..."
        if [catch {exec sddsconvert $file -pipe=out -retain=col,Time,SRDesiredMode,SRActualMode \
                     | sddscombine -pipe -merge \
                     | sddssort -pipe -col=Time \
                     | sddsprocess -pipe -nowarnings \
                     -filter=col,Time,$StartTime,$EndTime \
                     "-define=col,ScheduledUserOper,SRDesiredMode 1.0 == pop pop ? 1.0 : 0 $ " \
                     "-define=col,BeamDelivered,SRActualMode 4.0 == pop pop ? 1.0 : 0 $ " \
                     | sddsprocess -pipe=in $tmpfile.$index \
                     "-redefine=col,BeamDelivered,BeamDelivered ScheduledUserOper *" \
                 } result] {
            return -code error "processing $file: $result"
        }
        lappend fileList $tmpfile.$index
        incr index
    }
    APSAddToTmpFileList -ID downtime -fileList $fileList
    if [catch {eval exec sddscombine $fileList /tmp/SRBeamStatus-$StartYear-$StartMonth$StartDay:$StartHour.$EndYear-$EndMonth$EndDay:$EndHour -overwrite -merge} result] {
        return -code error $result
    }
    if {$withPlots} {
        exec sddsplot -col=Time,*Mode -legend \
          -ticks=xtime -graph=line,vary \
          /tmp/SRBeamStatus-$StartYear-$StartMonth$StartDay:$StartHour.$EndYear-$EndMonth$EndDay:$EndHour &
    } else {
        APSDeleteTmpFileList -ID downtime
    }
}

proc CalculateDownTimeWhenBeamAvailable {args} {
    set beamStatusFile ""
    set beamlineFile ""
    APSParseArguments {beamStatusFile beamlineFile}
    
    global withPlots
    set tmpfile /tmp/[APSTmpString]
    APSAddToTmpFileList -ID downtime -fileList "$tmpfile $tmpfile.1 $tmpfile.2 $tmpfile.3 $tmpfile.4"
    SetStatus "Interpolating beam status time..."
    if [catch {exec sddsinterp $beamlineFile $tmpfile \
                 -col=Time,Value -fileValues=$beamStatusFile,col=Time,parallel } result] {
        return -code error $result
    }
    SetStatus "Calculating down time ..."
    if [catch {exec sddsxref $tmpfile $beamStatusFile -pipe=out \
                 -equate=Time -take=ScheduledUserOper,BeamDelivered \
                 | sddsprocess -pipe=in $tmpfile.1 \
                 "-define=col,BeamlineDown,Value 1.0 == pop pop ? 0 : 1 $ " \
                 "-define=col,DownWithSchedule,ScheduledUserOper Value - sto tmpVar pop tmpVar 1.0 == pop pop ? 1 : 0 $" \
                 "-define=col,DownWithDeliver,BeamDelivered Value - sto tmpVar pop tmpVar 1.0 == pop pop ? 1 : 0 $" \
             } result] {
        return -code error $result
    }
     #in $tmpfile .1 the value (0 or 1) column Value, Value1, and Value2 are:
    #                               0                1
    #Value       beamline          down           available  (from beamlineFile)
    #ScheduledUserOper             no             yes
    #DownWithSchedule              no             yes (ScheduledUserOper(=1) - Value(=0))
    #BeamDelivered                 no             yes
    #DownWithDeliver               no             yes (BeamDelivered(=1) - Value(=0) )
    #so integrate Value2 versus time will get the beamline down time when SR beam is available
    #use Time data from SR beam status file
    set downTimeList ""
    foreach col {BeamlineDown DownWithSchedule DownWithDeliver} {
        APSAddToTmpFileList -ID 1 -fileList $tmpfile.$col
        if [catch {exec sddsconvert $tmpfile.1 -pipe=out -retain=col,Time,$col \
                     | sddsbreak -pipe -changeOf=$col \
                     | sddsprocess -pipe -process=$col,first,IsDown \
                     -process=Time,spread,DownTime \
                     | sddsprocess -pipe=in $tmpfile.$col -nowarnings \
                     -filter=par,IsDown,1.0,1.0 } result] {
            return -code error $result
        }
        set downtimes [exec sdds2stream -par=DownTime $tmpfile.$col]
        set result 0
        foreach time $downtimes {
            set result [expr $result + $time]
        }
        lappend downTimeList [expr $result/3600.0]
    }
  
    #for plotting purpose separating Value,Value1 and Value2
    if [catch {exec sddsprocess $tmpfile.1 $tmpfile.2 \
                 "-redefine=col,ScheduledUserOper,ScheduledUserOper 0.8 *" \
                 "-redefine=col,BeamDelivered,BeamDelivered 0.7 *" \
                 "-redefine=col,DownWithSchedule,DownWithSchedule 0.6 *" \
                 "-redefine=col,DownWithDeliver,DownWithDeliver 0.4 *" } result] {
        return -code error $result
    }
    if {$withPlots} {
        exec sddsplot -ticks=xtime -grap=line,vary $tmpfile.2 \
          "-title=[file tail $beamlineFile] down time (the non-zero value is actually 1.0)" \
          -col=Time,Value -legen=spec=[file tail $beamlineFile] \
          -col=Time,ScheduledUserOper "-legen=spec=Scheduled User Operation" \
          -col=Time,BeamDelivered "-legen=spec=Beam Delivered" \
          -col=Time,DownWithSchedule  -legend \
          -col=Time,DownWithDeliver -legend  &
    } else {
        APSDeleteTmpFileList -ID downtime
    }
    return $downTimeList
}

proc ComputeDownTimeFromFile {args} {
    set file ""
    set valueName ""
    APSParseArguments {file valueName}
    
    set time [exec sdds2stream $file -col=Time]
    set downTimeList ""
    foreach name $valueName {
        set result 0
        set first 1
        set value [exec sdds2stream $file -col=$name]
        foreach t $time val $value {
            if {!$val} {
                set first 1
                continue
            }
            if {$val} {
                if {$first} {
                    set tt1 $t
                    set first 0
                } else {
                    set result [expr $result + $t - $tt1]
                    set tt1 $t
                }
            }
        }
        lappend downTimeList [expr $result / 3600]
    }
    return $downTimeList
}

set logDirectory /home/helios/oagData/logonchange/EPS
set status ""
set itemList {ID BM}
set sectorList ""
set missingList ""
if [catch {GetBeamlineList} result] {
    puts stderr $result
    exit
}

APSScrolledStatus .status -parent .userFrame -textVariable status -width 60 \
  -withButtons 1

APSSRSectorButtons .beamline -parent .userFrame -rootname beamline \
  -orientation horizontal -sectorList $sectorList \
  -label "Beamline" -description "Beamline selections" \
  -itemList $itemList -packOption "-side top" \
  -itemLabelList $itemList -sectorControl 1 \
  -missingList $missingList

MakeDateTimeFrame .time -parent .userFrame

set withPlots 0
APSRadioButtonFrame .plot -parent .userFrame -orientation horizontal \
  -label "With plots: " -buttonList {Yes No} -valueList {1 0} -variable withPlots \
  -contextHelp "Choose yes to plot time - value plots for each beamline or no for no plots."
APSButton .cal -parent .userFrame -text "Show Down Time" -command "CalculateDownTime"

APSGetVariableNameOfSRSectorButtons -sectorList $sectorList \
  -itemList $itemList -rootname beamline \
  -arrayName BeamlineArray

#unselect all
foreach name [array name BeamlineArray] {
    global $BeamlineArray($name)
    set $BeamlineArray($name) 0
}
