#! /usr/local/bin/python
### SimPages.py, by Ilya A. Zemskov(zemskov@univer.omsk.su)

"""
 About this model
**********************************************************************
 Моделирование набора страниц
 - Единица времени равняется 10msc (милисекунд)
 - Размер страницы измеряется в байтах
**********************************************************************

"""

__version__ = '$ Revision: 0.3.0 $ Date: 2003/05/31 13:21:00 $'

from __future__ import generators
from math import log
from time import localtime, strftime
from random import Random,expovariate,uniform,randint,random
import MySQLdb
from ConfigParser import *
from os import getcwd
import sys

#--[ Simulation variables ]------------------------------------
ChangeDistTab = [[1,0.083],[2,0.208],[3,0.333],[4,0.499],[5,0.749],[6,1]]
BadChanges = [1,2,3]
WasntChange = [6]
#---------------------------------------------------------------
comini = "..\\ini\\common.ini"
dbini = "..\\ini\\db.ini"

#--[ Special variables ]----------------------------------------
try:
    taskdir = sys.argv[1]
except IndexError, msg:
    sys.exit("Error: missing parameter - name of task directory")

try:
    taskinifile = sys.argv[2]
except IndexError, msg:
    sys.exit("Error: missing parameter - name of ini file")

comcfg = ConfigParser()
comcfg.read(comini)
wdir = comcfg.get('Options','WorkDirectory')

dbcfg = ConfigParser()
dbcfg.read(dbini)

cfg = ConfigParser()
cfg.read(wdir+taskdir+'\\ini\\'+taskinifile)

sercfg = ConfigParser()
sercfg.read(wdir+taskdir+'\\ini\\'+cfg.get('serOptions','CommonIni'))

flog = file(sercfg.get('logOptions','LogDirectory')+
          sercfg.get('logOptions','LogFileName'),
          "a+")

dbconn = MySQLdb.Connect(dbcfg.get('dbOptions','Host'),
                         dbcfg.get('dbOptions','Login'),
                         dbcfg.get('dbOptions','Password'),
                         cfg.get('dbOptions','DBName'))

TRACETOFILE = sercfg.getint('logOptions','LogTraceToFile')
RANDOMSEED = cfg.getint('simOptions','RandomSeed')

simvarsdefault = {\
    'BeginPagesSumSize'   :0}
#---------------------------------------------------------------

def tablelookup(tabl):
    r = grv.random()
    #print "tablelookup at %s random %7.6f"%(now(), r)
    last = 0
    for x in tabl:
        if r>=last and r<x[1]:
            return x[0]
        else:
            last = x[1]
    putfile("gluk randoma %7.4f"%(r,))
    return x[0]

def expont(theta):
    r = grv.random()
    return -theta*log(r);

def putfile(st):
    if TRACETOFILE:
        flog.write(st+"\n")
        flog.flush()
    else:
        print st

def clearsimvars():
    for key in simvars:
        simvars[key] = 0

#--[ Simulation classes ]----------------------------------------
class Page:
    """ 
    """
    def __init__(self, conn, iter, pnum):
        self.size = grv.randint(sercfg.getint('pageOptions','MinPageSize'),
                                sercfg.getint('pageOptions','MaxPageSize'))
        curs = conn.cursor()
        curs.execute("insert into %s values('%d', '%s','%d','%d','%d','%d','%d','%d')"%
                     (dbcfg.get('dbOptions','PagesTableName'),
                      pnum,
                      iter,
                      sercfg.getint('pageOptions','DefaultPageChange'),
                      self.size,
                      self.size,
                      sercfg.getint('pageOptions','DefaultPageChanged'),
                      sercfg.getint('pageOptions','DefaultPageCrawled'),
                      sercfg.getint('simOptions','ModelTime')))
        self.id = pnum
        curs.close()

class Change:
    """ 
    """
    def __init__(self, conn, pid, traceout=0):
        self.pid = pid
        self.name = 'ChangePage'+`self.pid`
        self.traceout = traceout
        self.conn = conn
        curs = self.conn.cursor()
        curs.execute("select * from %s where pid=%d"%
                     (dbcfg.get('dbOptions','PagesTableName'),
                      self.pid))
        self.page = curs.fetchone()
        self.size = self.page[3]
        self.prevchange = 6
        self.newchange = 6
        curs.close()
        self.life()
        
    def life(self):
        curs = self.conn.cursor()
        t = 0 #Begin time is zero
        tend = sercfg.getint('simOptions','ModelTime')
        while t<=tend:
            ChTime = int(expont(sercfg.getint('simOptions','ModelTime')/
                                cfg.getfloat('changeOptions','ChangeIntensity')))#grv.expovariate(ModelTime/Changes)
            self.prevchange = self.newchange
            self.newchange = tablelookup(ChangeDistTab)
                
            if self.newchange==4:
                b = self.size - sercfg.getint('pageOptions','MinPageSize')
                if b<=0:
                    self.newchange = 6
                else:
                    deltasize = grv.randint(cfg.getint('changeOptions','MinPageDecSize'),b)
                    self.size = self.size - deltasize
                    self.trace("new pagesize is %s"% self.size)
            elif self.newchange==5:
                b =  sercfg.getint('pageOptions','MaxPageSize') - self.size
                if b<=0:
                    self.newchange = 6
                else:
                    deltasize = grv.randint(cfg.getint('changeOptions','MinPageIncSize'),b)
                    self.size = self.size + deltasize
                    self.trace("new pagesize is %s"% self.size)
            else:
               pass

            curs.execute("insert into %s values('', '%s','%d','%d','%d','%d','%d')"%
                         (dbcfg.get('dbOptions','ChangesTableName'),
                          self.page[1],
                          self.pid,
                          t+ChTime,
                          self.newchange,
                          self.prevchange,
                          self.size))
            t += ChTime
        curs.close()


    def trace(self,message):
        if self.traceout:
            putfile("at %7.4f %s %s"%\
                  (now() , self.name, message))

class Query:
    """ 
    """
    def __init__(self, conn, pid, traceout=0):
        self.pid = pid
        self.name = 'QueryPage'+`self.pid`
        self.traceout = traceout
        self.conn = conn
        curs = self.conn.cursor()
        curs.execute("select * from %s where pid=%d"%
                     (dbcfg.get('dbOptions','PagesTableName'),
                      self.pid))
        self.page = curs.fetchone()
        curs.close()
        self.life()
        
    def life(self):
        curs = self.conn.cursor()
        t = 0 #Begin time is zero
        tend = sercfg.getint('simOptions','ModelTime')
        while t<=tend:
            QryTime = int(expont(sercfg.getint('simOptions','ModelTime')/
                                cfg.getfloat('requestOptions','RequestIntensity')))#grv.expovariate(ModelTime/Queries)

            curs.execute("insert into %s values('', '%d','%d','%d')"%
                         (dbcfg.get('dbOptions','QueriesTableName'),
                          self.page[1],
                          self.pid,
                          t+QryTime))
            t += QryTime
        curs.close()


    def trace(self,message):
        if self.traceout:
            putfile("at %7.4f %s %s"%\
                  (now() , self.name, message))
#======================================================================================
  
def main():
    global grv, simvars, dbconn, cfg

    grv = Random(RANDOMSEED)
    putfile("-=[ Simulation Log, started %s ]=-"%strftime("%d.%m.%Y %H:%M:%S", localtime()))
    putfile("---[ SimPages "+__version__+" ]--")
    putfile("-< Used %s configuration file"%(wdir+taskdir+'\\ini\\'+taskinifile))

    curs = dbconn.cursor()
# Create Pages table
    try:
        curs.execute("drop table %s"%dbcfg.get('dbOptions','PagesTableName'))
    except MySQLdb.Error,msg:
        print "MySQL error:",msg
        pass
    curs.execute("create table %s(%s)"\
                 %(dbcfg.get('dbOptions','PagesTableName'),\
                   dbcfg.get('dbOptions','PagesTableFieldsSQL')))
# Create Events table
    try:
        curs.execute("drop table %s"%dbcfg.get('dbOptions','ChangesTableName'))
    except MySQLdb.Error,msg:
        print "MySQL error:",msg
        pass
    curs.execute("create table %s(%s)"\
                 %(dbcfg.get('dbOptions','ChangesTableName'),\
                   dbcfg.get('dbOptions','ChangesTableFieldsSQL')))
# Create Queries table
    try:
        curs.execute("drop table %s"%dbcfg.get('dbOptions','QueriesTableName'))
    except MySQLdb.Error,msg:
        print "MySQL error:",msg
        pass
    curs.execute("create table %s(%s)"\
                 %(dbcfg.get('dbOptions','QueriesTableName'),\
                   dbcfg.get('dbOptions','QueriesTableFieldsSQL')))
    
    curs.close()

    for j in range(sercfg.getint('simOptions','Iterations')):
        jj = j+1
        putfile("----[ Run: "+`jj`+" ]------------------------------------------------")
        simvars = {}
        simvars = simvarsdefault.copy()

        grv.seed(RANDOMSEED+jj)
        putfile("-< Random seed is %s"%(RANDOMSEED+jj))
        
        putfile("-< Run page generation")
        i = 1
        n = 1
        while i<=sercfg.getint('pageOptions','PageCount'):
            p = Page(dbconn, jj, n)
            simvars['BeginPagesSumSize'] += p.size
            c = Change(dbconn, p.id, 0)
            q = Query(dbconn, p.id, 0)
            if i.__mod__(sercfg.getint('logOptions','StepLogCreatingPages'))==0 :
                putfile("-< %s pages created at %s"%(i, strftime("%H:%M:%S", localtime())))
            i += 1
            n += 1

        print "%d pages complete "%(i-1)
        putfile("-< Pages generation complete")
        putfile("-----[ Statistics ]-------------------------------------------")
        for key in simvars:
            putfile("-< %s = %d"%(key, simvars[key]))
        putfile("-----[ Statistics ]-------------------------------------------")
        putfile("----[ Run: "+`jj`+" ]------------------------------------------------")
        clearsimvars()
        putfile("\n")
        print "\n"

    putfile("---[ SimPages "+__version__+" ]--")
    putfile("-=[ Simulation Log, finished %s ]=-\n"%strftime("%d.%m.%Y %H:%M:%S", localtime()))
    flog.close()    
    dbconn.close()

#--[ Main program :) ]------------------------------------------
if __name__ == '__main__':
    main()
