#!/usr/bin/env python
#                       Test/CellFactory.py - Copyright rwa2
#
# This file was generated on %date% at %time%
#
# Parametric model of a pool of employees and employers who live at various transit-connected nodes

import psyco
psyco.log()
psyco.profile()

import string

from SimPy.Simulation import *
from SimPy.SimPlot import *
from random import *
from GeneralClasses import *
from TransportationInfrastructure import *

skillcode = {
                1 : "description of jobcode 1",      
                2 : "description of jobcode 2",
                3 : "description of jobcode 3"
        }

class WorkShift:
        "Hours and days worked"
        def __init__(self, start_time, end_time, days, cycle):
                self.st = start_time
                self.et = end_time      #
                self.d = days           # days which these times are in effect (for a 7day week, 0=mon, 1=tues, etc.)
                self.c = cycle          # days before schedule repeats (7 for a weekly schedule)


# List of exampe work schedules
WorkSchedule = [
    WorkShift(9,17,range(5),7),
    WorkShift(7,15,range(5),7),
    WorkShift(16,0,range(5),7)
    ]


class Employee(Individual):
    "A worker with skill codes capable of filling a job vacancy"
    def __init__(self,parent):
        Individual.__init__(self,parent)
        self.skillset = []    # list of skillcodes this person possesses, really only just useful for distinguishing different kinds of work people mightdo
        self.workloc = []   # list of VacancyPools, representing jobs this person has and their schedules
    def live(self):
        """"Do the things we do, over and over"""
        global h2ts
        global WorkSchedule
        while True:
    ##        self.sleep()
    ##        self.commuteToWork()
    ##        self.commuteHome()
    ##    def commuteToWork(self):
            """Demand to go to work location"""
            print "%7.4f %s : Going to work"%(now(),self.name)
            commutetime = 60   # FIXME: assume we have a one hour commute for now
            # Find next departure time for work referring to vacancypool
            t=now()
            cycle = WorkSchedule[self.workloc.schedule].c
            t = t % (7 * 24 * h2ts)   # weekly schedule repeats
            for i in WorkSchedule[self.workloc.schedule].d:  # find next workday
                st = (WorkSchedule[self.workloc.schedule].st + (i * 24)) * h2ts
                if t < st:
                    yield hold, self, st-t  # leave for work
                    self.relocate(self.workloc.p)
                    print "%7.4f %s : At work"%(now(),self.name)
                    break
    ##    def commuteHome(self):
            """Demand to return to home location"""
            print "%7.4f %s : Going home"%(now(),self.name)
            global h2ts
            commutetime = 60   # FIXME: assume we have a one hour commute for now
            # Find next departure time for work referring to vacancypool
            global WorkSchedule
            t=now()
            cycle = WorkSchedule[self.workloc.schedule].c
            t = t % (7 * 24 * h2ts)   # weekly schedule repeats
            for i in WorkSchedule[self.workloc.schedule].d:  # find next workday
                et = (WorkSchedule[self.workloc.schedule].et + (i * 24)) * h2ts
                if t < et:
                    yield hold, self, et-t  # leave for home
                    self.relocate(self.residence[0])
                    print "%7.4f %s : At home"%(now(),self.name)
                    break
 
class WorkShift:
        "Hours and days worked"
        def __init__(self, start_time, end_time, days, cycle):
                self.st = start_time
                self.et = end_time     
                self.d = days           # days which these times are in effect (for a 7day week, 0=mon, 1=tues, etc.)
                self.c = cycle          # days before schedule repeats (7 for a weekly schedule)       

# WorkSchedules assumes that the simulation always starts at midnight Monday morning.
WorkSchedule = [
        WorkShift(9,17,range(5),7),
        WorkShift(7,15,range(5),7),
        WorkShift(16,0,range(5),7)
        ]

class VacancyPool:
    "Job vacancies and shift schedule.  Each group has the same job code and working schedule"
    def __init__(self,parent,numPositions,skillcode,schedule):
        self.p = parent     # employer with these vacancies
        self.n = numPositions   # number of vacancies available
        self.skillcode = skillcode  # int index to a particular skill code
        self.schedule = schedule    # int index into the global WorkSchedule list
    def createEmployees(self):
        """Create and returns an array of worker objects"""
        peeps = []
        for i in range(self.n):       # TODO: - allow us to be understaffed
            peeps.append(Employee(self.p))        # Create the employee, initially attach them to the employer object
            peeps[i].skillset.append(self.skillcode)        # Forcibly assume they already have the skills
            # Give them a pointer to this vacancypool so they can look up where they work and when
            peeps[i].workloc = self
        return peeps        
    

class Employer(Process,Cell):
    "Workplace with a list of job vacancies with specific job skillcodes and shift times"
    def __init__(self,parent,i):
        Process.__init__(self,name='Emp'+str(i))
        Cell.__init__(self,parent)
        self.vp = []    # prepare a vacancy pool of job positions
        


def randomNode(cell):
    """ Return a random subcell of the provided cell """
    return cell.sc[randint(0,len(cell.sc)-1)]
    

def createEmployers(cell,numEmployers=1):
    "Return an list of Employer objects with a distribution of jobcodes and schedules"
    emp = []
    for  i in range(numEmployers):
        ##emp.append(Employer(name = "Emp%02d"%(i,)))
        emp.append(Employer(cell,i))
        emp[i].vp.append(VacancyPool(emp[i],10,skillcode=1,schedule=1))
        print "Created employer ", emp[i].name
        # Create array of workers to fill vacancies
        workers = emp[i].vp[len(emp[i].vp)-1].createEmployees()
        # Create homes at some node for those workers to live
        global c
        for w in workers:
            w.getAHome(randomNode(c))
        ##emp[i].vp.append(VacancyPool(50,skillcode=2,schedule=2))
        ##emp[i].vp.append(VacancyPool(10,skillcode=3,schedule=2))
    return (emp)
    

def model(SEED=101010):
    "Initialize the scenario"
    seed(SEED)
    
    global c
    c = Cell(None)  # the master cell, has no parent "None"
    for i in range(5):  # create this many location nodes in the master cell
        c.sc.append(Cell(c))
        c.sc[i].sc.extend(createEmployers(c,randint(0,5)))   # create employers distributed randomly across Cell nodes
        # also loops through Employers and distribute Employee individuals across Cell nodes
        
    # print  what this looks like
    ##print c.printtree()
    
    # create layers of transit infrastructure between Cell nodes
    # simplest layer - direct src to dest for debuggins
    # road layer - network of cars linked by geography, constrained by parametric congestion effects and parking lot sizes
    # light rail layer - network of trains running fixed routes between stations
    # bus layer - runs fixed routes / schedules on road networks through neighborhood Cells to link to light rail and other stops
    # taxi / vanpool layer - runs between road-connected Cells as requested
    # PRT layer - runs through neighborhood Cells on demand
    # aircraft layer - runs between airport nodes
    # water pipeline layer
    # sewage pipeline layer
    # electric grid layer
    # information distribution layer: uni- and bi- directional

    # set up simulation model and begin executing events
    initialize()
    for n in c.sc:      # each node
        for res in n.sc:    # each ResidenceCell
            if string.find(str(res),'ResidenceCell') > 0:
                for p in res.sc:    # each Individual
                    activate(p,p.live())
    simulate(until=72*h2ts)  #24 hours at a time scale of 1 minute

    # export to schedule optimization engine, execute and read results

    # simulate transit of vehicles

    # add unexpected breakdowns / events and trigger a recalculation of schedule



# Run the model once with the default random seed
createTransitGraph(8)
model()

"""
# Plot monitored results
Histo = Mon.histogram(low=0.0,high=1400.0,nbins=20)
plt=SimPlot()
hst=plt.plotHistogram(Histo,xlab='Time (min)',
    title = "Time spent sleeping",
    color="red", width=2)
hst.postscr("sleeptime.ps")
plt.mainloop()
"""