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

# $Log: not supported by cvs2svn $
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)]
APSStandardSetup

set args $argv
set allowAllSectors 0
APSStrictParseArguments {allowAllSectors ignoreLock}
set CVSRevisionAuthor "\$Revision: 1.74 $ \$Author: shang $"

set sectorList ""
set IDSteering(sectorList) ""

set configDir /home/helios/oagData/sr/knobs/IDbump/lattices/default
set sectorList  [join [exec sdds2stream -col=Sector /home/helios/oagData/sr/IDs/sectors.sdds]]
foreach type {x xp y yp} {
    set ${type}SteerLimit [exec sdds2stream -col=${type}SteerLimit /home/helios/oagData/sr/IDs/sectors.sdds]
}
foreach sector $sectorList x $xSteerLimit y $ySteerLimit xp $xpSteerLimit yp $ypSteerLimit {
    set sectorf [format %02d $sector]
    set IDSteering(S$sectorf.xlimit) [format %.0f $x]
    set IDSteering(S$sectorf.ylimit) [format %.0f $y]
    set IDSteering(S$sectorf.xplimit) [format %.0f $xp]
    set IDSteering(S$sectorf.yplimit) [format %.0f $yp]
}
puts $sectorList
set IDSteering(sectorList) $sectorList

set allSectors ""
for {set sector 1} {$sector<=40} {incr sector} {
    set sectorf [format %02d $sector]
    set IDSteering(S$sectorf.configLoaded) 0
    set IDSteering(S$sectorf.start) 0
    set IDSteering(S$sectorf.checked) 0
    set IDSteering(S$sectorf.limitStart) 0
    if [lsearch -exact $sectorList $sector]<0 {
	set IDSteering(S$sectorf.xlimit) 50
	set IDSteering(S$sectorf.ylimit) 50
	set IDSteering(S$sectorf.xplimit) 25
	set IDSteering(S$sectorf.yplimit) 25
    }
    lappend allSectors $sector
}


APSApplication . -name APSUIDSteeringServer \
  -overview "provides server controls for ID steering."

set tcl_precision 6

set IDSteeringStatus Ready.
APSScrolledStatus .status -parent .userFrame -width 50\
        -textVariable IDSteeringStatus 

proc SetIDSteeringStatus {text} {
    global IDSteeringStatus
    set IDSteeringStatus "[clock format [clock seconds] -format %H:%M:%S]: $text"
    update
    #    bell
}

proc MakeSectorsWidget {widget args} {
    global IDSteering allowAllSectors checkbuttonList

    set parent ""
    APSParseArguments {parent}
  
    set w $parent$widget
    APSFrame $widget -parent $parent \
      -label "Storage Ring ID selection for starting steering server." \
      -contextHelp {ID selection frame} 

    
    set buttonList ""
    set varList ""
    set commList ""
    for {set i 1} {$i<=40} {incr i} {
	set sectorf [format %02d $i]
	lappend buttonList $sectorf
	lappend varList IDSteering(S$sectorf.checked)
	lappend commList "MakeLimitWidget -sectorf $sectorf"
    }

    if {0} {
	set checkbuttonList [APSCheckButtonFrame .check -parent $w.frame -label "" -buttonList $buttonList -variableList $varList \
				 -orientation horizontal -limitPerRow 10 -allNone 1 -commandList $commList]
    }
    APSFrameGrid .grid -parent $w.frame -xList {x1 x2 x3 x4 x5}
    set w1 $w.frame.grid.x1
    set w2 $w.frame.grid.x2
    set w3 $w.frame.grid.x3
    set w4 $w.frame.grid.x4
    set w5 $w.frame.grid.x5
    set checkbuttonList ""
    set j 0
    foreach sectorf $buttonList var $varList comm $commList {
	set sector [scan $sectorf %ld]
	set i [expr $sector%5]
	if {$i==0} {
	    set i 5
	}
	APSFrame .f$j -parent [set w$i] -label ""
	set f [set w$i].f$j.frame
	$f configure  -bd 0 -relief flat
	set button [APSCheckButtonFrame .c$sectorf -parent $f -label "" -buttonList $sectorf -packOption "-side left" -variableList $var]
	APSButton .c1$sectorf -parent $f -text "limit" -size small -command $comm -packOption "-side left"
	lappend checkbuttonList $f.c$sectorf
	set IDSteering(S$sectorf.checkbutton) $button
	$button configure -bg grey90
	
	incr j
    }
    APSFrame .fa -parent $w.frame -label ""
    $w.frame.fa.frame configure -bd 0 -relief flat
    
    APSButton .all -parent $w.frame.fa.frame -text "All" -size small -command "SelectAll -value 1"
    APSButton .none -parent $w.frame.fa.frame -text "None" -size small -command "SelectAll -value 0"
    
    if !$allowAllSectors {
	for {set i 1} {$i<=40} {incr i} {
	    set button [lindex $checkbuttonList [expr $i-1]]
	    if [lsearch -exact $IDSteering(sectorList) $i]<0 {
		APSDisableWidget $button
	    }
	}
    }
    APSRadioButtonFrame .allow -parent $w.frame -label "Allow all sectors?" -buttonList {Yes No} \
	-valueList {1 0} -orientation horizontal -variable allowAllSectors \
	-commandList {EnableDisableSectors EnableDisableSectors}
    APSLabeledEntry .runtime -parent $w.frame -label "Server run time (0 means infinite) (seconds):" -width 15 \
	-textVariable IDSteering(runTime)
    APSLabeledEntry .stepsize -parent $w.frame -label "Corrector change limit per step(A):" -width 15 \
	-textVariable IDSteering(corrStepSize) \
	-contextHelp "if it is smaller than the individual knob step size, it will overwrite that knob's step size"
    bind $w.frame.stepsize.entry <Return> "UpdateStepSize"
   
    APSLabeledEntry .pause -parent $w.frame -label "Wait time (seconds) between two steps:" -width 15 \
	-textVariable IDSteering(pauseTime) -contextHelp "if it is longer than the individaul knob wait time, it will overwrite that knob's wait time."
    bind $w.frame.pause.entry <Return> "UpdateStepSize"

    APSButton .start -parent $w.frame -text "Start" -command "StartIDSteeringServer" -contextHelp "starting the ID steering knobs"
    APSButton .abort -parent $w.frame -text "Abort" -command "KillIDSteeringServer" -contextHelp "kill the ID steering knobs"
}

proc EnableDisableSectors {args} {
    global checkbuttonList allowAllSectors IDSteering

    for {set i 1} {$i<=40} {incr i} {
	set button [lindex $checkbuttonList [expr $i-1]]
	if [lsearch -exact $IDSteering(sectorList) $i]<0 {
	    if !$allowAllSectors {
		set IDSteering(S[format %02d $i].checked) 0
		APSDisableWidget $button
	    } else {
		APSEnableWidget $button
	    }
	} else {
	    APSEnableWidget $button
	}
    }
}

set IDSteering(startedList) ""
set IDSTeering(startedPID) ""
set IDSteering(statusPV) ""
proc StartIDSteeringServer {args} {
    global IDSteering  checkbuttonList startedList statusPVList configDir
    set knobList ""
    set statusPVList ""
    set statusVarList ""
    set startedList ""
    for {set sector 1} {$sector<=40} {incr sector} {
	set sectorf [format %02d $sector]
	set button [lindex $checkbuttonList [expr $sector -1]]
	if !$IDSteering(S$sectorf.checked) {
	    continue
	}
	if $IDSteering(S$sectorf.start) {
	    SetIDSteeringStatus "Sector $sector already started."
	    continue
	}
	if [catch {LoadConfig -sector $sector} result] {
	    return -code error "Error loading config for sector $sector: $result"
	}
	foreach knobName $IDSteering(S$sectorf.knobList) {
	    if [catch {exec pvget $knobName} result] {
		#doest not exist, ok
	    } else {
		if ![APSYesNoPopUp "$knobName already exist, destroy it and re-create?"] {
		    SetIDSteeringStatus "$knobName aready exist, skip"
		    continue
		}
		exec daq-pv-group destroy --group-channel $knobName
		after 3000
	    }
	    SetIDSteeringStatus "creating $knobName..."
	    if [catch {exec daq-pv-group create --input-sdds-file $IDSteering($knobName.configFile) \
			   --group-channel $knobName --runtime $IDSteering(runTime) \
			   --group-update-period 0.5  & } result] {
		SetIDSteeringStatus "Error creating $knobName: $result"
		APSAlertBox .alert -errorMessage "Error creating $knobName : $result"
		return
	    }
	    lappend startedPID $result
	    lappend startedList $knobName
	    lappend statusPVList $IDSteering($knobName.statusPV)
	    lappend statusVarList IDSteering($knobName.status)
	    lappend knobList $knobName
	    lappend readbackPVLIst $IDSteering($knobName.readbackPV)
	    lappend readbackVarList IDSteering($knobName.readbackValue)
	}
	set IDSteering(S$sectorf.start) 1
	$IDSteering(S$sectorf.checkbutton) configure -bg green
	SetIDSteeringStatus "S$sectorf ID steering server started."
    }
    if ![llength $startedList] {
	SetIDSteeringStatus "No new server started."
	return
    }
    set IDSteering(startedList) [lsort -unique $startedList]
    after 5000
    pv linkw $statusVarList $statusPVList
    pv linkw $readbackVarList $readbackPVLIst
    
    foreach knobName $startedList {
	set readbackPV $IDSteering($knobName.readbackPV)
	set limit $IDSteering($knobName.limit)
	if [catch {exec cavput -list=${readbackPV}.DRVH=$limit,${readbackPV}.DRVL=[expr -1.0*$limit] -pend=10} result] {
	    return -code error "Error setting drive hight and low for $readbackPV: $result"
	}
	
	if [catch {exec cavput -list=$IDSteering($knobName.limitPV)=$IDSteering($knobName.stepSize),$IDSteering($knobName.pausePV)=$IDSteering($knobName.pause) -pend=10 } result] {
	    return -code error "error setting step size and pause for $knobName: $result"
	}
	
    }
    foreach var $statusVarList {
	pv umon $var
    }
    pv getw $readbackVarList
    foreach var $readbackVarList {
	pv umon $var
    }
    SetIDSteeringStatus "[join $knobList ,] started."
}

proc SelectAll {args} {
    set value 1
    APSParseArguments {value}
    global IDSteering allowAllSectors
    for {set sector 1} {$sector<=40} {incr sector} {
	set sectorf [format %02d $sector]
	if !$allowAllSectors {
	    if [lsearch -exact $IDSteering(sectorList) $sector]<0 {
		#do nothing
	    } else {
		set IDSteering(S$sectorf.checked) $value
	    }
	} else {
	    set IDSteering(S$sectorf.checked) $value
	}
    }
}

proc KillIDSteeringServer {args} {
    global IDSteering checkbuttonList configDir
   
    set delList ""
    for {set sector 1} {$sector<=40} {incr sector} {
	set sectorf [format %02d $sector]
	set button [lindex $checkbuttonList [expr $sector -1]]
	if !$IDSteering(S$sectorf.checked) {
	    continue
	}
	
	foreach knobName $IDSteering(S$sectorf.knobList) {
	    if [catch {exec pvget $knobName} result] {
		SetIDSteeringStatus "$knobName does not exist!"
		continue
	    }
	    SetIDSteeringStatus "destroy $knobName..."
	    exec daq-pv-group destroy --group-channel $knobName
	    lappend delList $sector
	}
	set IDSteering(S$sectorf.start) 0
	$IDSteering(S$sectorf.checkbutton) configure -bg grey90
    }
    set delList [lsort -unique -integer $delList]
    if [llength $delList] {
	SetIDSteeringStatus "Steering sever of sector [join $delList] is aborted."
    }
    return
    
    set IDSteering(startedPID) ""
    if ![llength $IDSteering(startedList)] {
	SetIDSteeringStatus "No server started."
	return
    }
    foreach knob $IDSteering(startedList) {
	SetIDSteeringStatus "destroy $knob..."
	exec daq-pv-group destroy --group-channel $knob
    }
    set IDSteering(startedList) ""
    set IDSteering(statusPV) ""
    SetIDSteeringStatus "steering server aborted."
}

set tmpRoot /tmp/[APSTmpString]
proc LoadConfig {args} {
    global IDSteering tmpRoot configDir
    set sector ""
    APSParseArguments {sector}
    
    set sectorf [format %02d $sector]
    set file $configDir/ID$sectorf.cokn
    if ![file exist $file] {
	return -code error "Config file ($file) does not exist for Sector $sector."
    }
    set pages [exec sdds2stream -npages=bar $file]
    set IDSteering(S$sectorf.knobList) ""
    for {set page 1} {$page<=$pages} {incr page} {
	if [catch {exec sddsconvert $file $tmpRoot.S$sectorf.$page -from=$page -to=$page} result] {
	    return -code error "Error in sddsconvert: $result"
	}
	APSAddToTmpFileList -ID idstring -fileList $tmpRoot.S$sectorf.$page
	set config $tmpRoot.S$sectorf.$page 
	set knobName [exec sdds2stream $config -par=ControlName]
#	puts $knobName
	lappend IDSteering(S$sectorf.knobList) $knobName
	set IDSteering($knobName.configFile) $config
	APSAddToTmpFileList -ID idstree -fileList $tmpRoot.S$sectorf.$page
	set statusPV [exec sdds2stream $config -par=SetStatusPV]
	set readbackPV [exec sdds2stream -par=SetRbvPV $config]
	set type [exec sdds2stream -par=KnobType $config]
	set limitPV [exec sdds2stream -par=SetValueLimitPV $config]
	set pausePV [exec sdds2stream -par=SetValueWaitTimePV $config]
	set stepSize [exec sdds2stream -par=DeltaLimit $config]
	set pause [exec sdds2stream -par=WaitTime $config]
	set IDSteering(${knobName}.limit)  $IDSteering(S$sectorf.${type}limit)
	set IDSteering(${knobName}.lowLimit) [expr -1.0 * $IDSteering(S$sectorf.${type}limit)]
	set IDSteering(${knobName}.highLimit) $IDSteering(S$sectorf.${type}limit)
	set IDSteering(${knobName}.readbackPV) $readbackPV
	set IDSteering(${knobName}.statusPV) $statusPV
	set IDSteering(${knobName}.status) ""
	set IDSteering(${knobName}.limitPV) $limitPV
	set IDSteering(${knobName}.stepSize) $stepSize
	set IDSteering(${knobName}.pausePV) $pausePV
	set IDSteering(${knobName}.pause) $pause
	puts "$IDSteering(${knobName}.highLimit) "
    }
}

proc UpdateStepSize {args} {
    global IDSteering
    set varList ""
    for {set sector 1} {$sector<=40} {incr sector} {
	if $IDSteering(corrStepSize)<$IDSteering($knob.stepSize) {
	    set IDSteering($knob.stepSize) $IDSteering($corrStepSize)
	}
	if $IDSteering(pauseTime)>$IDSteeirng($knob.pause)  {
	    set IDSteering($knob.pause) $IDSteering(pauseTime)
	}
    }
    for {set sector 1} {$sector<=40} {incr sector} {
	if !$IDSteering(S[format %02d sector].start) {
	    continue
	}
	foreach knob $IDSteering(S[format %02d $sector].knobList) {
	    lappend varList IDSteering($knob.stepSize)
	    lappend varList IDSteering($knob.pause)
	}
    }
    if [llength $varList] {
	pv putw $varList
    }
}

proc MakeLimitWidget {args} {
    global IDSteering
    set sectorf ""
    APSParseArguments {sectorf}
    
   
    set width 15
    
    set border 2
    set dialogFrame .dialogID$sectorf.userFrame
    if [winfo exist .dialogID$sectorf] {
	destroy .dialogID$sectorf
    }
    APSDialogBox .dialogID$sectorf  \
	-name "ID$sectorf limit Dialog" \
	-contextHelp "Dialog box for steering limit of ID$sectorf." 
    
    
    APSFrame .parameters -parent $dialogFrame 
    $dialogFrame.parameters.frame configure -relief flat
    set w $dialogFrame.parameters.frame
    
    set pvList ""
    set varList ""
    APSLabel .label -parent $w -text "                                Low Limit       High Limit    Steered Value"
    foreach knob $IDSteering(S$sectorf.knobList) {
	if [regexp {xp} $knob] {
	    set type xp
	    set unit urad
	} elseif [regexp {yp} $knob] {
	    set type yp
	    set unit urad
	} elseif [regexp {x} $knob] {
	    set type x
	    set unit um
	} else {
	    set type y
	    set unit um
	}
	puts "$knob $type $IDSteering($knob.lowLimit) $IDSteering($knob.lowLimit) "
	APSLabeledEntryFrame .a$type -parent $w -label "ID$sectorf $type ($knob) ($unit):" \
	    -width $width -variableList [list IDSteering($knob.lowLimit) IDSteering($knob.highLimit) IDSteering($knob.readbackValue)] -orientation horizontal
	lappend varList IDSteering($knob.lowLimit) 
	lappend varList IDSteering($knob.highLimit)
	
	lappend pvList $IDSteering($knob.readbackPV).DRVL
	lappend pvList $IDSteering($knob.readbackPV).DRVH
	bind $w.a$type.frame.entry1 <Return> "UpdateLimit -sectorf $sectorf"
	bind $w.a$type.frame.entry2 <Return> "UpdateLimit -sectorf $sectorf"
	APSButton .zero$type -parent $w.a$type -packOption "-side right"  -text "reset" -size small -command "ResetSteering -knob $knob" \
	    -contextHelp "reset the steering readback value to zero."
    }
    if {$IDSteering(S$sectorf.start) && !$IDSteering(S$sectorf.limitStart)} {
	pv linkw $varList $pvList
	pv putw $varList

	foreach var $varList {
	    pv umon $var
	}
    }
}

proc ResetSteering {args} {
    global IDSteering
    set knob ""
    APSParseArguments {knob}
    set IDSteering($knob.readbackValue) 0
    pv putw IDSteering($knob.readbackValue)
    
}

proc UpdateLimit {args} {
    global IDSteering
    set sectorf ""
    APSParseArguments {sectorf}
    if !$IDSteering(S$sectorf.start) {
	return -code error "Sector $sectorf steering server have not started yet, can not change its steering limit!"
    }
    foreach knob $IDSteering(S$sectorf.knobList) {
	pv putw IDSteering($knob.lowLimit)
	pv putw IDSteering($knob.highLimit)
    }
}

for {set sector 1} {$sector<=40} {incr sector} {
    puts "Loading sector $sector config..."
    LoadConfig -sector $sector
}

set IDSteering(runTime) 0
set IDSteering(corrStepSize) 5
set IDSteering(bpmStepSize) 3
set IDSteering(pauseTime) 1
#set wList [APSTabFrame .tab -parent .userFrame -labelList {SteeringSever SteeringLimit} -height 400]
#set w1 [lindex $wList 0]
#set w2 [lindex $wList 1]
MakeSectorsWidget .sectors -parent .userFrame

