#!/usr/bin/env esp Thread.checkpoint.clear raise "#{ESP::Name} does not use cartridges" unless defined? Cartridge ShellScript[File.basename(__FILE__, '.rb').intern] = script = proc do nonstop = partial = mail = processing = false $sched = [] condition = proc{true} progName = File.basename $0 progArgs = ARGV.dup require 'getoptlong' $grace = 30.minutes spares=Cartridge.spares options=Cartridge.options mailcfg = File.which "phasecfg.rb", ESP::Path GetoptLong.new( ["--wait", "-w", GetoptLong::REQUIRED_ARGUMENT], ["--interval", "-i", GetoptLong::REQUIRED_ARGUMENT], ["--schedule", "-s", GetoptLong::REQUIRED_ARGUMENT], ["--grace", "-g", GetoptLong::REQUIRED_ARGUMENT], ["--nonstop", "-n", GetoptLong::NO_ARGUMENT], ["--partial", "-p", GetoptLong::NO_ARGUMENT], ["--replaceProcessing", "-r", GetoptLong::REQUIRED_ARGUMENT], ["--processOnly", "-y", GetoptLong::REQUIRED_ARGUMENT], ["--options", "-o", GetoptLong::REQUIRED_ARGUMENT], ["--first", "-f", GetoptLong::REQUIRED_ARGUMENT], ["--mail", "-m", GetoptLong::NO_ARGUMENT], ["--types", "-t", GetoptLong::NO_ARGUMENT], ["--slots", "-l", GetoptLong::NO_ARGUMENT], ["--checkpoints", "-c", GetoptLong::REQUIRED_ARGUMENT], ["--help", "-h", GetoptLong::NO_ARGUMENT] ).each do |opt, arg| case opt when "--options" options = eval "(c=Cartridge).options.clear; c.options #{arg}", TOPLEVEL_BINDING, opt when "--first" eval arg, binding, opt when "--nonstop" nonstop = true when "--partial" partial = true when "--replaceProcessing" processing = proc{eval arg} when "--processOnly" condition = proc{eval arg} when "--mail" mail = true when "--checkpoints" Thread.adjust :MaxCheckpoints, Integer(arg) when "--wait" for waitStr in arg.split ',' $sched << waitStr.seconds end when "--interval" for intervalStr in arg.split ',' interval = intervalStr.seconds raise ArgumentError, "interval of #{interval} <= 0" if interval<=0 $sched << (-interval).seconds end when "--schedule" now = Thread.time for dayTime in arg.split ',' t = ParseDate.instant dayTime, now #just to validate time spec raise ArgumentError, "#{t} is in the past" if t < now $sched << dayTime #String rather than Time in case of daylight savings end when "--grace" $grace = Delay arg when "--types" index = 0 hash = {} Cartridge.types.each {|t| hash[t]=(index-=1)} hash.delete :unknown puts "\nCartridge type: index for LRAUV", hash Process.exit 3 when "--slots" Log.comment < Intervals are represented as as negative --wait times Note: 'dry' or 'wet' may be used in place of type specify *any* dry/wet cartridge. Examples: #{progName} --wait=90 #wait 90 seconds between cartridges #{progName} --wait=-3:: #process cartridges at 3 hour interval #{progName} --sched=9AM,9PM lyseHold_bac archive_bac #two groups of two cartridges per day -- first at 9AM, then at 9PM. #{progName} --wait=2days --sched=9AM,9PM lyseHold_bac archive_bac #wait 2 days, sample two groups of two cartridges per day, wait 2 days... #{progName} --replace=nil #skip processing (filter only) #{progName} --replace=throwPVreagent #skip processing & depressurize cartridge The --processOnly option allows one to skip filter processing on certain carts #{progName} --process=Cart.type==:daSPR_zoo #process only :daSPR_zoo carts #{progName} --process="Cart.type.to_s.start_with? 'a'" #only archival carts END Process.exit 1 end end if mail and not ::ESP.require('email', mailcfg, 'yelp').empty? ::ESP.module_eval <<-END module_function def flushMail flushMail = Thread(:flushMail) {Email.flush} unless flushMail.join 30 Log.comment "Sending Email" flushMail.join 300 or Log.recordException Email::Error.new "flush stuck!" end uploading = Log.uploading #wait for last upload to complete unless uploading.join 30 Log.comment "Uploading Logs" uploading.join 600 or Log.recordException Log::Error.new "upload stuck!" end end atExit {flushMail} END end def cartSpecId specStr #return cartridge type or state identifier given String or false if invalid specStr = specStr[1..-1] if specStr.ord == ?: cartId = specStr.intern (Cartridge.type? cartId or Cartridge.state? cartId) and cartId end Sampler.maxPSIa += 50 rescue Log.comment "Failed to increase Sampler's MaxPSIa!" filterArgs = [] #default to empty arg list group = [] #list of cartridge types in cartridge group maxVol = 0 if firstArg = ARGV[0] begin maxVol = Float firstArg raise ArgumentError, "0 max sample volume is invalid" if maxVol == 0 i = 0 rescue raise ArgumentError, "'#{firstArg}' is neither a valid volume nor a cartridge type or state" unless firstType = cartSpecId(firstArg) i = -1 end while cartSpecStr = ARGV[i+=1] cartSpec = cartSpecId(cartSpecStr) or Cartridge.raiseTypeOrStateError cartSpecStr group << cartSpec end end if maxVol == 0 Log.comment "Sampling default goal volumes" else Log.comment "Sampling maximum of #{maxVol.abs}ml per cartridge" Log.comment "** Not priming cartridges **" if maxVol < 0 filterArgs << maxVol.abs end group<<:dry if group.empty? #default to next dry cartridge missionTxt = < # of cartridges of that type in group sortedGroup = group.sort lastSpec = sortedGroup.first qty = 0 sortedGroup.each do |cartSpec| if cartSpec == lastSpec qty += 1 else specs[lastSpec] = qty lastSpec = cartSpec qty = 1 end end specs[lastSpec] = qty specs end def checkInventory groupSpec #verify we have enough of each required cartridge type for the next group groupSpec.each do |cartSpec, qty| typ,state = Cartridge.state?(cartSpec) ? [nil,cartSpec]:[cartSpec,:dry] raise Cartridge::None, "Not enough #{[state,typ].compact.join ' ' } cartridges remain" if Cartridge.loaded(typ, state).length < qty end end def delayGroupStart group, start, late=0 if late <= 0 power = Power.core ? "Waiting":"Shutdown" GoodNews.mail "Sampling next #{group.join ', '} cartridge#{ 's' if group.length != 0 }", Subject:"#{power} until #{start.asShortString}" if defined? GoodNews delayUntil start else Log.record "Running #{late.round.seconds} late" raise Delay::TooLate, "#{start} is > #{$grace.seconds} past" if late>$grace end end groupSpec = partial ? {group.first => 1} : groupCarts(group) $schedIndex = $sched.length #loops around sched array begin loop do #repeat until no dry cartridges remain checkInventory groupSpec $schedIndex = 0 if $schedIndex >= $sched.length tspec = $sched[$schedIndex] if tspec.is_a? String #await time of day now = Thread.time late = now - (start = ParseDate.instant tspec, now) delayGroupStart group, start, late end lastStartTime = Thread.time for cartSpec in group Cmd.loadCartridge cartSpec if Cmd.state != :PROCESS #no filtering :standard cartridges Sampler.skipPrime if maxVol < 0 GoodNews.mail "Filtering up to #{filterArgs.empty? ? Cartridge.sampler.goalVolume : filterArgs.first}ml#{ " without priming" if maxVol<0}", Subject:"Sampling #{Cartridge.type} cartridge in slot #{ Cartridge.selected}" if mail Cmd.filter *filterArgs end if processing processing.call else GoodNews.mail "After filtering #{Cmd.status[:volumeFiltered]}ml", Subject:"Processing #{Cartridge.type } cartridge in slot #{Cartridge.selected}" if mail Cmd.process end if condition[Cartridge.type] end Cmd.stop unless nonstop if tspec and not tspec.is_a? String #delay or interval between groups checkInventory groupSpec if tspec < 0 #group sampling interval late = Thread.time - (nextStart = lastStartTime - tspec) delayGroupStart group, nextStart, late else #simple delay between groups delayGroupStart group, Thread.time+tspec end end $schedIndex += 1 end rescue Cartridge::None=>done Log.recordException done end end #script script.call if $0 == __FILE__ #run script if invoked from OS shell