/*
 * Title:        CloudSim Toolkit
 * Description:  CloudSim (Cloud Simulation) Toolkit for Modeling and Simulation of Clouds
 * Licence:      GPL - http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2009-2010, The University of Melbourne, Australia
 */

package org.cloudbus.cloudsim;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.cloudbus.cloudsim.core.CloudSim;
import org.cloudbus.cloudsim.core.CloudSimTags;
import org.cloudbus.cloudsim.core.SimEntity;
import org.cloudbus.cloudsim.core.SimEvent;

/**
 * 处理vm请求
 * Datacenter class is a CloudResource whose hostList
 * are virtualized. It deals with processing of VM queries (i.e., handling
 * of VMs) instead of processing Cloudlet-related queries. So, even though an
 * AllocPolicy will be instantiated (in the init() method of the superclass,
 * it will not be used, as processing of cloudlets are handled by the CloudletScheduler
 * and processing of VirtualMachines are handled by the VmAllocationPolicy.
 *
 * @author  Rodrigo N. Calheiros
 * @author  Anton Beloglazov
 * @since  CloudSim Toolkit 1.0
 */
public class Datacenter extends SimEntity {

 /** The characteristics. 查找主机,主机状态数量和pe状态数量等信息*/
 private DatacenterCharacteristics characteristics;

 /** The regional cis name. */
 private String regionalCisName;

 /** The vm provisioner. vm分配策略*/
 private VmAllocationPolicy vmAllocationPolicy;

 /** The last process time. */
 private double lastProcessTime;

 /** The debts. 欠款*/
 private Map<Integer, Double> debts;

 /** The storage list. */
 private List<Storage> storageList;

 /** The vm list. */
 private List<? extends Vm> vmList;

 /** The scheduling interval. 调度事件间隔*/
 private double schedulingInterval;

 /**
  * Allocates a new PowerDatacenter object.
  *
  * @param name       the name to be associated with this entity (as
  * required by Sim_entity class from simjava package)
  * @param characteristics   an object of DatacenterCharacteristics
  * @param storageList a LinkedList of storage elements, for data simulation
  * @param vmAllocationPolicy the vmAllocationPolicy
  *
  * @throws Exception This happens when one of the following scenarios occur:
  * <ul>
  * <li> creating this entity before initializing CloudSim package
  * <li> this entity name is <tt>null</tt> or empty
  * <li> this entity has <tt>zero</tt> number of PEs (Processing
  * Elements). <br>
  * No PEs mean the Cloudlets can't be processed.
  * A CloudResource must contain one or more Machines.
  * A Machine must contain one or more PEs.
  * </ul>
  *
  * @pre name != null
  * @pre resource != null
  * @post $none
  */
 public Datacenter(String name, DatacenterCharacteristics characteristics, VmAllocationPolicy vmAllocationPolicy, List<Storage> storageList, double schedulingInterval) throws Exception {
  super(name);

  setCharacteristics(characteristics);
  setVmAllocationPolicy(vmAllocationPolicy);
  setLastProcessTime(0.0);
  setDebts(new HashMap<Integer,Double>());
  setStorageList(storageList);
  setVmList(new ArrayList<Vm>());
  setSchedulingInterval(schedulingInterval);
  
  for (Host host: getCharacteristics().getHostList()) {
   host.setDatacenter(this);
  }

        // If this resource doesn't have any PEs then no useful at all
        if (getCharacteristics().getPesNumber() == 0) {
            throw new Exception(super.getName() + " : Error - this entity has no PEs. Therefore, can't process any Cloudlets.");
        }

        // stores id of this class
        getCharacteristics().setId(super.getId());
 }

    /**
     * Overrides this method when making a new and different type of resource.
     * This method is called by {@link #body()} to register other type to
     * GIS entity. In doing so, you
     * need to create a new child class extending from
     * gridsim.CloudInformationService.
     * <br>
     * <b>NOTE:</b> You do not need to override {@link #body()} method, if
     * you use this method.
     *
     * @pre $none
     * @post $none
     */
    protected void registerOtherEntity() {
        // empty. This should be override by a child class
    }

    /**
     * 处理实体发送过来的是事件,返回他们询问的信息或者做某些操作
     * Processes events or services that are available for this PowerDatacenter.
     *
     * @param ev    a Sim_event object
     *
     * @pre ev != null
     * @post $none
     */
    @Override
 public void processEvent(SimEvent ev) {
        int srcId = -1;
        //Log.printLine(CloudSim.clock()+"[PowerDatacenter]: event received:"+ev.getTag());

        switch (ev.getTag()) {
            // Resource characteristics inquiry
            case CloudSimTags.RESOURCE_CHARACTERISTICS:
                srcId = ((Integer) ev.getData()).intValue();
                sendNow(srcId, ev.getTag(), getCharacteristics());
                break;

                // Resource dynamic info inquiry
            case CloudSimTags.RESOURCE_DYNAMICS:
                srcId = ((Integer) ev.getData()).intValue();
                sendNow(srcId, ev.getTag(), 0);
                break;

            case CloudSimTags.RESOURCE_NUM_PE:
                srcId = ((Integer) ev.getData()).intValue();
                int numPE = getCharacteristics().getPesNumber();
                sendNow(srcId, ev.getTag(), numPE);
                break;

            case CloudSimTags.RESOURCE_NUM_FREE_PE:
                srcId = ((Integer) ev.getData()).intValue();
                int freePesNumber = getCharacteristics().getFreePesNumber();
                sendNow(srcId, ev.getTag(), freePesNumber);
                break;

                // New Cloudlet arrives,处理cloudlet提交事件,不需要回复确认
            case CloudSimTags.CLOUDLET_SUBMIT:
                processCloudletSubmit(ev, false);
                break;

                // 处理cloudlet提交事件,需要回复确认New Cloudlet arrives, but the sender asks for an ack
            case CloudSimTags.CLOUDLET_SUBMIT_ACK:
                processCloudletSubmit(ev, true);
                break;

                // Cancels a previously submitted Cloudlet,取消事件
            case CloudSimTags.CLOUDLET_CANCEL:
                processCloudlet(ev, CloudSimTags.CLOUDLET_CANCEL);
                break;

                // Pauses a previously submitted Cloudlet,暂停
            case CloudSimTags.CLOUDLET_PAUSE:
                processCloudlet(ev, CloudSimTags.CLOUDLET_PAUSE);
                break;

                // Pauses a previously submitted Cloudlet, but the sender
                // asks for an acknowledgement
            case CloudSimTags.CLOUDLET_PAUSE_ACK:
                processCloudlet(ev, CloudSimTags.CLOUDLET_PAUSE_ACK);
                break;

                // Resumes a previously submitted Cloudlet恢复
            case CloudSimTags.CLOUDLET_RESUME:
                processCloudlet(ev, CloudSimTags.CLOUDLET_RESUME);
                break;

                // Resumes a previously submitted Cloudlet, but the sender
                // asks for an acknowledgement
            case CloudSimTags.CLOUDLET_RESUME_ACK:
                processCloudlet(ev, CloudSimTags.CLOUDLET_RESUME_ACK);
                break;

                // Moves a previously submitted Cloudlet to a different resource移交到另一个资源的vm上
            case CloudSimTags.CLOUDLET_MOVE:
                processCloudletMove((int[]) ev.getData(), CloudSimTags.CLOUDLET_MOVE);
                break;

                // Moves a previously submitted Cloudlet to a different resource
            case CloudSimTags.CLOUDLET_MOVE_ACK:
                processCloudletMove((int[]) ev.getData(), CloudSimTags.CLOUDLET_MOVE_ACK);
                break;

                // Checks the status of a Cloudlet查看cloudlet的状态
            case CloudSimTags.CLOUDLET_STATUS:
                processCloudletStatus(ev);
                break;

                // Ping packet
            case CloudSimTags.INFOPKT_SUBMIT:           //ping请求
                processPingRequest(ev);
                break;
               
                //vm创建
            case CloudSimTags.VM_CREATE:
          processVmCreate(ev, false);
          break;

         case CloudSimTags.VM_CREATE_ACK:
          processVmCreate(ev, true);
          break;
          
            case CloudSimTags.VM_DESTROY:                //vm销毁
             processVmDestroy(ev, false);
             break;

            case CloudSimTags.VM_DESTROY_ACK:         
          processVmDestroy(ev, true);
          break;
      
            case CloudSimTags.VM_MIGRATE:                //vm迁移
             processVmMigrate(ev, false);
             break;

            case CloudSimTags.VM_MIGRATE_ACK:
             processVmMigrate(ev, true);
             break;

            case CloudSimTags.VM_DATA_ADD:            //文件添加
             processDataAdd(ev, false);
             break;

            case CloudSimTags.VM_DATA_ADD_ACK:
             processDataAdd(ev, true);
             break;

            case CloudSimTags.VM_DATA_DEL:           //文件销毁
             processDataDelete(ev, false);
             break;

            case CloudSimTags.VM_DATA_DEL_ACK:
             processDataDelete(ev, true);
             break;

            case CloudSimTags.VM_DATACENTER_EVENT:         //处理cloudlet
             updateCloudletProcessing();
             checkCloudletCompletion();
             break;

             // other unknown tags are processed by this method
            default:
                processOtherEvent(ev);
                break;
        }
    }

    /**
     * 文件销毁
     * Process data del.
     *
     * @param ev the ev
     * @param ack the ack
     */
    protected void processDataDelete(SimEvent ev, boolean ack) {
        if (ev == null) {
            return;
        }

        Object[] data = (Object[]) ev.getData();
        if (data == null) {
            return;
        }

        String filename = (String) data[0];
        int req_source = ((Integer) data[1]).intValue();
        int tag = -1;

        // check if this file can be deleted (do not delete is right now)
        int msg = deleteFileFromStorage(filename);
        if (msg == DataCloudTags.FILE_DELETE_SUCCESSFUL) {
           tag = DataCloudTags.CTLG_DELETE_MASTER;
        } else { // if an error occured, notify user
           tag = DataCloudTags.FILE_DELETE_MASTER_RESULT;
        }

     if (ack){
            // send back to sender
            Object pack[] = new Object[2];
            pack[0] = filename;
            pack[1] = Integer.valueOf(msg);

            sendNow(req_source, tag, pack);
     }
 }

 /**
  * 处理添加文件
  * Process data add.
  *
  * @param ev the ev
  * @param ack the ack
  */
 protected void processDataAdd(SimEvent ev, boolean ack) {
       if (ev == null) {
            return;
        }

        Object[] pack = (Object[]) ev.getData();
        if (pack == null) {
            return;
        }

        File file = (File) pack[0]; // get the file
        file.setMasterCopy(true); // set the file into a master copy
        int sentFrom = ((Integer) pack[1]).intValue(); // get sender ID

        /******     // DEBUG
         Log.printLine(super.get_name() + ".addMasterFile(): " +
         file.getName() + " from " + CloudSim.getEntityName(sentFrom));
         *******/

        Object[] data = new Object[3];
        data[0] = file.getName();

        int msg = addFile(file); // 添加文件到一个存储器

        double debit;
        if (getDebts().containsKey(sentFrom)) {
         debit = getDebts().get(sentFrom);
        } else {
         debit = 0.0;
        }

        debit += getCharacteristics().getCostPerBw() * file.getSize();

        getDebts().put(sentFrom, debit);

        if (ack) {
         data[1] = Integer.valueOf(-1); // no sender id
         data[2] = Integer.valueOf(msg); // the result of adding a master file
         sendNow(sentFrom, DataCloudTags.FILE_ADD_MASTER_RESULT, data);
        }
 }

 /**
  * ping请求,pkt直接输出
  * Processes a ping request.
  *
  * @param ev  a Sim_event object
  *
  * @pre ev != null
  * @post $none
  */
    protected void processPingRequest(SimEvent ev) {
        InfoPacket pkt = (InfoPacket) ev.getData();
        pkt.setTag(CloudSimTags.INFOPKT_RETURN);
        pkt.setDestId(pkt.getSrcId());

        // sends back to the sender
        sendNow(pkt.getSrcId(), CloudSimTags.INFOPKT_RETURN, pkt);
    }

    /**
     * 询问一个cloudlet的状态
     * Process the event for an User/Broker who wants to know the status of a Cloudlet.
     * This PowerDatacenter will then send the status back to the User/Broker.
     *
     * @param ev   a Sim_event object
     *
     * @pre ev != null
     * @post $none
     */
    protected void processCloudletStatus(SimEvent ev) {
        int cloudletId = 0;
        int userId = 0;
        int vmId = 0;
        int status = -1;

        try{
            // if a sender using cloudletXXX() methods
            int data[] = (int[]) ev.getData();
            cloudletId = data[0];
            userId = data[1];
            vmId = data[2];

            status = getVmAllocationPolicy().getHost(vmId, userId).getVm(userId, vmId).getCloudletScheduler().getCloudletStatus(cloudletId);
        }

        // if a sender using normal send() methods
        catch (ClassCastException c) {
            try {
                Cloudlet cl = (Cloudlet) ev.getData();
                cloudletId = cl.getCloudletId();
                userId = cl.getUserId();

                status = getVmAllocationPolicy().getHost(vmId, userId).getVm(userId, vmId).getCloudletScheduler().getCloudletStatus(cloudletId);
            }
            catch (Exception e) {
                Log.printLine(getName() +
                        ": Error in processing CloudSimTags.CLOUDLET_STATUS");
                Log.printLine( e.getMessage() );
                return;
            }
        }
        catch (Exception e) {
            Log.printLine(getName() +
                    ": Error in processing CloudSimTags.CLOUDLET_STATUS");
            Log.printLine( e.getMessage() );
            return;
        }

        int[] array = new int[3];
        array[0] = getId();
        array[1] = cloudletId;
        array[2] = status;

        int tag = CloudSimTags.CLOUDLET_STATUS;
        sendNow(userId, tag, array);
    }

    /**
     * Here all the method related to VM requests will be received and forwarded to the related method.
     *
     * @param ev the received event
     *
     * @pre $none
     * @post $none
     */
    protected void processOtherEvent(SimEvent ev) {
        if (ev == null){
            Log.printLine(getName() + ".processOtherEvent(): Error - an event is null.");
        }
    }

    /**
     * 创建一个虚拟机
     * Process the event for an User/Broker who wants to create a VM
     * in this PowerDatacenter. This PowerDatacenter will then send the status back to
     * the User/Broker.
     *
     * @param ev   a Sim_event object
     * @param ack the ack
     *
     * @pre ev != null
     * @post $none
     */
 protected void processVmCreate(SimEvent ev, boolean ack) {
     Vm vm = (Vm) ev.getData();
     
     //在主机上分配vm
      boolean result = getVmAllocationPolicy().allocateHostForVm(vm);

      if (ack) {
         int[] data = new int[3];
           data[0] = getId();
         data[1] = vm.getId();

           if (result) {
            data[2] = CloudSimTags.TRUE;
           } else {
            data[2] = CloudSimTags.FALSE;
           }
     sendNow(vm.getUserId(), CloudSimTags.VM_CREATE_ACK, data);
      }

      if (result) {
   double amount = 0.0;                        //记录收费
   if (getDebts().containsKey(vm.getUserId())) {
    amount = getDebts().get(vm.getUserId());
   }
   amount += getCharacteristics().getCostPerMem() * vm.getRam();
   amount += getCharacteristics().getCostPerStorage() * vm.getSize();

   getDebts().put(vm.getUserId(), amount);

   getVmList().add(vm);
   
   //新虚拟机,先处理一下cloudlet任务,即使没有任务
   vm.updateVmProcessing(CloudSim.clock(), getVmAllocationPolicy().getHost(vm).getVmScheduler().getAllocatedMipsForVm(vm));
      }

    }

 /**
  * 销毁虚拟机
  * Process the event for an User/Broker who wants to destroy a VM
  * previously created in this PowerDatacenter. This PowerDatacenter may send,
  * upon request, the status back to the User/Broker.
  *
  * @param ev   a Sim_event object
  * @param ack the ack
  *
  * @pre ev != null
  * @post $none
  */
    protected void processVmDestroy(SimEvent ev, boolean ack) {
  Vm vm = (Vm) ev.getData();
  getVmAllocationPolicy().deallocateHostForVm(vm);

  if (ack) {
   int[] data = new int[3];
   data[0] = getId();
   data[1] = vm.getId();
   data[2] = CloudSimTags.TRUE;

   sendNow(vm.getUserId(), CloudSimTags.VM_DESTROY_ACK, data);
  }

  getVmList().remove(vm);
    }

    /**
     * 把一个vm移入到一个新的host上
     * Process the event for an User/Broker who wants to migrate a VM.
     * This PowerDatacenter will then send the status back to the User/Broker.
     * @param ev   a Sim_event object
     * @pre ev != null
     * @post $none
     */
 protected void processVmMigrate(SimEvent ev, boolean ack) {
  Object tmp = ev.getData();
  if (!(tmp instanceof Map<?, ?>)) {
   throw new ClassCastException("The data object must be Map<String, Object>");
  }

  @SuppressWarnings("unchecked")
  Map<String, Object> migrate = (HashMap<String, Object>) tmp;

  Vm vm = (Vm) migrate.get("vm");
  Host host = (Host) migrate.get("host");

  getVmAllocationPolicy().deallocateHostForVm(vm);
  host.removeMigratingInVm(vm);
  boolean result = getVmAllocationPolicy().allocateHostForVm(vm, host);
  if (!result) {
   Log.printLine("Allocation failed");
  }

  if (ack) {
   int[] data = new int[3];
   data[0] = getId();
   data[1] = vm.getId();

   if (result) {
    data[2] = CloudSimTags.TRUE;
   } else {
    data[2] = CloudSimTags.FALSE;
   }
   sendNow(ev.getSource(), CloudSimTags.VM_CREATE_ACK, data);
  }

  double amount=0.0;
  if (debts.containsKey(vm.getUserId())) {
   amount = debts.get(vm.getUserId());
  }

  amount += getCharacteristics().getCostPerMem() * vm.getRam();
  amount += getCharacteristics().getCostPerStorage() * vm.getSize();

  debts.put(vm.getUserId(), amount);

  Log.formatLine("%.2f: Migration of VM #%d to Host #%d is completed", CloudSim.clock(), vm.getId(), host.getId());
  vm.setInMigration(false);
    }

    /**
     * 根据事件类型(暂停,取消,回复)处理cloudlet
     * Processes a Cloudlet based on the event type.
     *
     * @param ev   a Sim_event object
     * @param type event type
     *
     * @pre ev != null
     * @pre type > 0
     * @post $none
     */
    protected void processCloudlet(SimEvent ev, int type) {
        int cloudletId = 0;
        int userId = 0;
        int vmId = 0;

        try { // if the sender using cloudletXXX() methods
            int data[] = (int[]) ev.getData();
            cloudletId = data[0];
            userId = data[1];
            vmId = data[2];
        }

        // if the sender using normal send() methods
        catch (ClassCastException c) {
            try {
                Cloudlet cl = (Cloudlet) ev.getData();
                cloudletId = cl.getCloudletId();
                userId = cl.getUserId();
                vmId = cl.getVmId();
            } catch (Exception e) {
                Log.printLine(super.getName() + ": Error in processing Cloudlet");
                Log.printLine(e.getMessage());
                return;
            }
        } catch (Exception e) {
            Log.printLine(super.getName() + ": Error in processing a Cloudlet.");
            Log.printLine( e.getMessage() );
            return;
        }

        // begins executing ....
  switch (type) {
   case CloudSimTags.CLOUDLET_CANCEL:                   //取消事件
    processCloudletCancel(cloudletId, userId, vmId);
    break;

   case CloudSimTags.CLOUDLET_PAUSE:
    processCloudletPause(cloudletId, userId, vmId, false);
    break;

   case CloudSimTags.CLOUDLET_PAUSE_ACK:
    processCloudletPause(cloudletId, userId, vmId, true);
    break;

   case CloudSimTags.CLOUDLET_RESUME:
    processCloudletResume(cloudletId, userId, vmId, false);
    break;

   case CloudSimTags.CLOUDLET_RESUME_ACK:
    processCloudletResume(cloudletId, userId, vmId, true);
    break;
   default:
    break;
  }

    }

    /**
     * 任务完成不移交
     * 移交到本地datacenter,移交到外地的datacenter
     * Process the event for an User/Broker who wants to move a Cloudlet.
     *
     * @param receivedData   information about the migration
     * @param type  event tag
     *
     * @pre receivedData != null
     * @pre type > 0
     * @post $none
     */
    protected void processCloudletMove(int[] receivedData, int type) {
     updateCloudletProcessing();

     int[] array = receivedData;
     int cloudletId = array[0];
     int userId = array[1];
     int vmId = array[2];
     int vmDestId = array[3];
     int destId = array[4];

     //get the cloudlet
     Cloudlet cl = getVmAllocationPolicy().getHost(vmId, userId).getVm(userId, vmId).getCloudletScheduler().cloudletCancel(cloudletId);

     boolean failed=false;
  if (cl == null) {// cloudlet doesn't exist
   failed = true;
  } else {
   // has the cloudlet already finished?
   if (cl.getCloudletStatus() == Cloudlet.SUCCESS) {// if yes, send it back to user
    int[] data = new int[3];
    data[0] = this.getId();
    data[1] = cloudletId;
    data[2] = 0;
    sendNow(cl.getUserId(), CloudSimTags.CLOUDLET_SUBMIT_ACK, data);
    sendNow(cl.getUserId(), CloudSimTags.CLOUDLET_RETURN, cl);
   }

   // prepare cloudlet for migration
   cl.setVmId(vmDestId);

   // the cloudlet will migrate from one vm to another does the destination VM exist?
   if (destId == this.getId()) {
    Vm vm = getVmAllocationPolicy().getHost(vmDestId, userId).getVm(userId, vmDestId);
    if (vm == null) {
     failed = true;
    } else {
     double fileTransferTime = predictFileTransferTime(cl.getRequiredFiles()); // time to transfer the files
     vm.getCloudletScheduler().cloudletSubmit(cl, fileTransferTime);
    }
   } else {// the cloudlet will migrate from one resource to another
    int tag = ((type == CloudSimTags.CLOUDLET_MOVE_ACK) ? CloudSimTags.CLOUDLET_SUBMIT_ACK : CloudSimTags.CLOUDLET_SUBMIT);
    sendNow(destId, tag, cl);
   }
  }

  if (type == CloudSimTags.CLOUDLET_MOVE_ACK) {// send ACK if requested
   int[] data = new int[3];
   data[0] = this.getId();
   data[1] = cloudletId;
   if (failed) {
    data[2] = 0;
   } else {
    data[2] = 1;
   }
   sendNow(cl.getUserId(), CloudSimTags.CLOUDLET_SUBMIT_ACK, data);
  }
    }

    /**
     * 处理一个cloudlet提交,如果此cloudlet完成,发送确认;没完成,发送确认及预计完成时间
     * Processes a Cloudlet submission.
     *
     * @param ev  a SimEvent object
     * @param ack  an acknowledgement
     *
     * @pre ev != null
     * @post $none
     */
    protected void processCloudletSubmit(SimEvent ev, boolean ack) {
     updateCloudletProcessing();        //一个新cloudlet提交之前,先处理(更新)每个cloudlet状态

        try {
            // gets the Cloudlet object
            Cloudlet cl = (Cloudlet) ev.getData();

            // checks whether this Cloudlet has finished or not,任务已经完成
            if (cl.isFinished()){
                String name = CloudSim.getEntityName(cl.getUserId());
                Log.printLine(getName()+": Warning - Cloudlet #"+cl.getCloudletId()+" owned by "+name+" is already completed/finished.");
                Log.printLine("Therefore, it is not being executed again");
                Log.printLine();

                // NOTE: If a Cloudlet has finished, then it won't be processed.
                // So, if ack is required, this method sends back a result.
                // If ack is not required, this method don't send back a result.
                // Hence, this might cause CloudSim to be hanged since waiting
                // for this Cloudlet back.发送确认
                if (ack) {
                    int[] data = new int[3];
                    data[0] = getId();
                    data[1] = cl.getCloudletId();
                    data[2] = CloudSimTags.FALSE;

                    // unique tag = operation tag
                    int tag = CloudSimTags.CLOUDLET_SUBMIT_ACK;
                    sendNow(cl.getUserId(), tag, data);
                }

                sendNow(cl.getUserId(), CloudSimTags.CLOUDLET_RETURN, cl);

                return;
            }

            // process this Cloudlet to this CloudResource,cloudlet没有完成
            cl.setResourceParameter(getId(), getCharacteristics().getCostPerSecond(), getCharacteristics().getCostPerBw());

            int userId = cl.getUserId();
            int vmId = cl.getVmId();

            double fileTransferTime = predictFileTransferTime(cl.getRequiredFiles()); //time to transfer the files文件传送时间

            Host host = getVmAllocationPolicy().getHost(vmId, userId);
            Vm vm = host.getVm(vmId, userId);
            CloudletScheduler scheduler = vm.getCloudletScheduler();
            double estimatedFinishTime = scheduler.cloudletSubmit(cl,fileTransferTime); //提交如果在处理返回处理完成事件,如果等待返回0

            //if (estimatedFinishTime > 0.0 && estimatedFinishTime < getSchedulingInterval()) { //if this cloudlet is in the exec queue
            if (estimatedFinishTime > 0.0) { //if this cloudlet is in the exec queue
             //double estimatedFinishTime = (cl.getCloudletTotalLength()/(capacity*cl.getPesNumber())); //time to process the cloudlet
             //Log.printLine(estimatedFinishTime+"="+gl.getCloudletLength()+"/("+capacity+"*"+gl.getNumPE()+")");
             estimatedFinishTime += fileTransferTime;
             //estimatedFinishTime += CloudSim.clock();
             //Log.printLine(CloudSim.clock()+": Next event scheduled to +"+estimatedFinishTime);
             send(getId(), estimatedFinishTime, CloudSimTags.VM_DATACENTER_EVENT);
            }
           
            if (ack) {
                int[] data = new int[3];
                data[0] = getId();
                data[1] = cl.getCloudletId();
                data[2] = CloudSimTags.TRUE;

                // unique tag = operation tag
                int tag = CloudSimTags.CLOUDLET_SUBMIT_ACK;
                sendNow(cl.getUserId(), tag, data);
            }
        }
        catch (ClassCastException c) {
            Log.printLine(getName() + ".processCloudletSubmit(): " + "ClassCastException error.");
            c.printStackTrace();
        }
        catch (Exception e) {
            Log.printLine(getName() + ".processCloudletSubmit(): " + "Exception error.");
            e.printStackTrace();
        }

     checkCloudletCompletion();
    }

    /**
     * 文件传送时间
     * Predict file transfer time.
     *
     * @param requiredFiles the required files
     *
     * @return the double
     */
    protected double predictFileTransferTime(List<String> requiredFiles) {
  double time = 0.0;

  Iterator<String> iter = requiredFiles.iterator();
  while (iter.hasNext()) {
   String fileName = iter.next();
   for (int i = 0; i < getStorageList().size(); i++) {
    Storage tempStorage = getStorageList().get(i);
    File tempFile = tempStorage.getFile(fileName);
    if (tempFile != null) {
     time += tempFile.getSize() / tempStorage.getMaxTransferRate();
     break;
    }
   }
  }
  return time;
 }

 /**
  * 处理回复事件
  * Processes a Cloudlet resume request.
  *
  * @param cloudletId  resuming cloudlet ID
  * @param userId  ID of the cloudlet's owner
  * @param ack $true if an ack is requested after operation
  * @param vmId the vm id
  *
  * @pre $none
  * @post $none
  */
 protected void processCloudletResume(int cloudletId, int userId, int vmId, boolean ack) {
  double eventTime = getVmAllocationPolicy().getHost(vmId,userId).getVm(userId, vmId).getCloudletScheduler().cloudletResume(cloudletId);

  boolean status = false;
  if (eventTime > 0.0) { //if this cloudlet is in the exec queue
   status = true;
   if (eventTime > CloudSim.clock()) {
    schedule(getId(), eventTime, CloudSimTags.VM_DATACENTER_EVENT);
   }
        }

  if (ack) {
   int[] data = new int[3];
   data[0] = getId();
   data[1] = cloudletId;
   if (status) {
    data[2] = CloudSimTags.TRUE;
   } else {
    data[2] = CloudSimTags.FALSE;
   }
   sendNow(userId, CloudSimTags.CLOUDLET_RESUME_ACK, data);
  }
 }

 /**
  * 处理暂停事件
  * Processes a Cloudlet pause request.
  *
  * @param cloudletId  resuming cloudlet ID
  * @param userId  ID of the cloudlet's owner
  * @param ack $true if an ack is requested after operation
  * @param vmId the vm id
  *
  * @pre $none
  * @post $none
  */
 protected void processCloudletPause(int cloudletId, int userId, int vmId, boolean ack) {
  boolean status = getVmAllocationPolicy().getHost(vmId,userId).getVm(userId, vmId).getCloudletScheduler().cloudletPause(cloudletId);

  if (ack) {
   int[] data = new int[3];
   data[0] = getId();
   data[1] = cloudletId;
   if (status) {
    data[2] = CloudSimTags.TRUE;
   } else {
    data[2] = CloudSimTags.FALSE;
   }
   sendNow(userId, CloudSimTags.CLOUDLET_PAUSE_ACK, data);
  }
 }

 /**
  * 取消一个cloudlet,查找host,查找vm,查找cloudlet表取消,发送事件
  * Processes a Cloudlet cancel request.
  *
  * @param cloudletId  resuming cloudlet ID
  * @param userId  ID of the cloudlet's owner
  * @param vmId the vm id
  *
  * @pre $none
  * @post $none
  */
 protected void processCloudletCancel(int cloudletId, int userId, int vmId) {
  Cloudlet cl = getVmAllocationPolicy().getHost(vmId,userId).getVm(userId, vmId).getCloudletScheduler().cloudletCancel(cloudletId);

        sendNow(userId, CloudSimTags.CLOUDLET_CANCEL, cl);
 }

 /**
  * 对数据中心每个host,每个host对每个vm更新每个cloudlet的处理
  * Updates processing of each cloudlet running in this PowerDatacenter. It is necessary because
  * Hosts and VirtualMachines are simple objects, not entities. So, they don't receive events
  * and updating cloudlets inside them must be called from the outside.
  *
  * @pre $none
  * @post $none
  */
 protected void updateCloudletProcessing() {
  //Log.printLine(CloudSim.clock()+": PowerDatacenter #"+this.get_id()+": updating cloudlet processing.......................................");
  //if some time passed since last processing
  if (CloudSim.clock() > this.getLastProcessTime()) {
   List<? extends Host> list = getVmAllocationPolicy().getHostList();
   double smallerTime = Double.MAX_VALUE;
   //for each host...
   for (int i = 0; i < list.size(); i++) {
    Host host = list.get(i);
    double time = host.updateVmsProcessing(CloudSim.clock()); //inform VMs to update processing
    //what time do we expect that the next cloudlet will finish?
    if (time < smallerTime) {
     smallerTime = time;
    }
   }
   //schedules an event to the next time, if valid如果有效,发送给自己下个处理的时刻
   //当有任务加入,有任务结束的时候都需要处理
   //if (smallerTime > CloudSim.clock() + 0.01 && smallerTime != Double.MAX_VALUE && smallerTime < getSchedulingInterval()) {
   if (smallerTime > CloudSim.clock() + 0.01 && smallerTime != Double.MAX_VALUE) {
    schedule(getId(), (smallerTime - CloudSim.clock()), CloudSimTags.VM_DATACENTER_EVENT);
   }
   setLastProcessTime(CloudSim.clock());
  }
 }

 /**
  * 检查是否有任务完成
  * Verifies if some cloudlet inside this PowerDatacenter already finished.
  * If yes, send it to the User/Broker
  *
  * @pre $none
  * @post $none
  */
 protected void checkCloudletCompletion() {
  List<? extends Host> list = getVmAllocationPolicy().getHostList();
  for (int i = 0; i < list.size(); i++) {
   Host host = list.get(i);
   for (Vm vm : host.getVmList()) {
    while (vm.getCloudletScheduler().isFinishedCloudlets()){
     Cloudlet cl = vm.getCloudletScheduler().getNextFinishedCloudlet();
     if (cl != null) {
      sendNow(cl.getUserId(), CloudSimTags.CLOUDLET_RETURN, cl);
     }
    }
   }
  }
 }

   /**
    * 添加一个file到resource的存储器,在实验开始前,如果是主本,需要注册
    * Adds a file into the resource's storage before the experiment starts.
    * If the file is a master file, then it will be registered to the RC when
    * the experiment begins.
    *
    * @param file  a DataCloud file
    *
    * @return a tag number denoting whether this operation is a success or not
    *
    * @see CloudSim.datagrid.DataCloudTags#FILE_ADD_SUCCESSFUL
    * @see CloudSim.datagrid.DataCloudTags#FILE_ADD_ERROR_EMPTY
    */
    public int addFile(File file) {
        if (file == null) {
            return DataCloudTags.FILE_ADD_ERROR_EMPTY;
        }

        if (contains(file.getName())) {
            return DataCloudTags.FILE_ADD_ERROR_EXIST_READ_ONLY;
        }

        // check storage space first
        if (getStorageList().size() <= 0) {
            return DataCloudTags.FILE_ADD_ERROR_STORAGE_FULL;
        }

        Storage tempStorage = null;
        int msg = DataCloudTags.FILE_ADD_ERROR_STORAGE_FULL;

        for (int i = 0; i < getStorageList().size(); i++) {
            tempStorage = getStorageList().get(i);
            if (tempStorage.getAvailableSpace() >= file.getSize()) {
                tempStorage.addFile(file);
                msg = DataCloudTags.FILE_ADD_SUCCESSFUL;
                break;
            }
        }

        return msg;
    }

    /**
     * Checks whether the resource has the given file.
     *
     * @param file  a file to be searched
     *
     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
     */
    protected boolean contains(File file) {
        if (file == null) {
            return false;
        }
        return contains( file.getName() );
    }

    /**
     * 是否包含此文件
     * Checks whether the resource has the given file.
     *
     * @param fileName  a file name to be searched
     *
     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
     */
    protected boolean contains(String fileName) {
        if (fileName == null || fileName.length() == 0) {
            return false;
        }

        Iterator<Storage> it = getStorageList().iterator();
        Storage storage = null;
        boolean result = false;

        while (it.hasNext()) {
            storage = it.next();
            if (storage.contains(fileName)) {
                result = true;
                break;
            }
        }

        return result;
    }

    /**
     * 从存储器中销毁文件
     * Deletes the file from the storage. Also, check whether it is
     * possible to delete the file from the storage.
     *
     * @param fileName      the name of the file to be deleted
     *
     * @return the error message as defined in
     * {@link CloudSim.datagrid.DataCloudTags}
     *
     * @see CloudSim.datagrid.DataCloudTags#FILE_DELETE_SUCCESSFUL
     * @see CloudSim.datagrid.DataCloudTags#FILE_DELETE_ERROR_ACCESS_DENIED
     * @see CloudSim.datagrid.DataCloudTags#FILE_DELETE_ERROR
     */
    private int deleteFileFromStorage(String fileName) {
        Storage tempStorage = null;
        File tempFile = null;
        int msg = DataCloudTags.FILE_DELETE_ERROR;

        for (int i = 0; i < getStorageList().size(); i++) {
            tempStorage = getStorageList().get(i);
            tempFile = tempStorage.getFile(fileName);
            tempStorage.deleteFile(fileName, tempFile);
            msg = DataCloudTags.FILE_DELETE_SUCCESSFUL;
        } // end for

        return msg;
    }

 /**
  * Prints the debts.
  */
 public void printDebts() {
  Log.printLine("*****PowerDatacenter: "+this.getName()+"*****");
  Log.printLine("User id\t\tDebt");

  Set<Integer> keys = getDebts().keySet();
  Iterator<Integer> iter = keys.iterator();
  DecimalFormat df = new DecimalFormat("#.##");
  while (iter.hasNext()) {
   int key = iter.next();
   double value = getDebts().get(key);
   Log.printLine(key+"\t\t"+df.format(value));
  }
  Log.printLine("**********************************");
 }

 /* (non-Javadoc)
  * @see cloudsim.core.SimEntity#shutdownEntity()
  */
 @Override
 public void shutdownEntity() {
        Log.printLine(getName() + " is shutting down...");
 }

 /* (non-Javadoc)
  * @see cloudsim.core.SimEntity#startEntity()
  */
 @Override
 public void startEntity() {
  Log.printLine(getName() + " is starting...");
        // this resource should register to regional GIS.
        // However, if not specified, then register to system GIS (the
        // default CloudInformationService) entity.
        int gisID = CloudSim.getEntityId(regionalCisName);
        if (gisID == -1) {
            gisID = CloudSim.getCloudInfoServiceEntityId();
        }

        // send the registration to GIS
        sendNow(gisID, CloudSimTags.REGISTER_RESOURCE, getId());
        // Below method is for a child class to override
        registerOtherEntity();
 }

 /**
  * Gets the host list.
  *
  * @return the host list
  */
 @SuppressWarnings("unchecked")
 public <T extends Host> List<T> getHostList() {
  return (List<T>) getCharacteristics().getHostList();
 }

 /**
  * Gets the characteristics.
  *
  * @return the characteristics
  */
 protected DatacenterCharacteristics getCharacteristics() {
  return characteristics;
 }

 /**
  * Sets the characteristics.
  *
  * @param characteristics the new characteristics
  */
 protected void setCharacteristics(DatacenterCharacteristics characteristics) {
  this.characteristics = characteristics;
 }

 /**
  * Gets the regional cis name.
  *
  * @return the regional cis name
  */
 protected String getRegionalCisName() {
  return regionalCisName;
 }

 /**
  * Sets the regional cis name.
  *
  * @param regionalCisName the new regional cis name
  */
 protected void setRegionalCisName(String regionalCisName) {
  this.regionalCisName = regionalCisName;
 }

 /**
  * Gets the vm allocation policy.
  *
  * @return the vm allocation policy
  */
 public VmAllocationPolicy getVmAllocationPolicy() {
  return vmAllocationPolicy;
 }

 /**
  * Sets the vm allocation policy.
  *
  * @param vmAllocationPolicy the new vm allocation policy
  */
 protected void setVmAllocationPolicy(VmAllocationPolicy vmAllocationPolicy) {
  this.vmAllocationPolicy = vmAllocationPolicy;
 }

 /**
  * 获得上次处理事件
  * Gets the last process time.
  *
  * @return the last process time
  */
 protected double getLastProcessTime() {
  return lastProcessTime;
 }

 /**
  * Sets the last process time.
  *
  * @param lastProcessTime the new last process time
  */
 protected void setLastProcessTime(double lastProcessTime) {
  this.lastProcessTime = lastProcessTime;
 }

 /**
  * Gets the debts.
  *
  * @return the debts
  */
 protected Map<Integer, Double> getDebts() {
  return debts;
 }

 /**
  * Sets the debts.
  *
  * @param debts the debts
  */
 protected void setDebts(Map<Integer, Double> debts) {
  this.debts = debts;
 }

 /**
  * Gets the storage list.
  *
  * @return the storage list
  */
 protected List<Storage> getStorageList() {
  return storageList;
 }

 /**
  * Sets the storage list.
  *
  * @param storageList the new storage list
  */
 protected void setStorageList(List<Storage> storageList) {
  this.storageList = storageList;
 }

 /**
  * Gets the vm list.
  *
  * @return the vm list
  */
 @SuppressWarnings("unchecked")
 public <T extends Vm> List<T> getVmList() {
  return (List<T>) vmList;
 }

 /**
  * Sets the vm list.
  *
  * @param vmList the new vm list
  */
 protected <T extends Vm> void setVmList(List<T> vmList) {
  this.vmList = vmList;
 }

 /**
  * Gets the scheduling interval.
  *
  * @return the scheduling interval
  */
 protected double getSchedulingInterval() {
  return schedulingInterval;
 }

 /**
  * Sets the scheduling interval.
  *
  * @param schedulingInterval the new scheduling interval
  */
 protected void setSchedulingInterval(double schedulingInterval) {
  this.schedulingInterval = schedulingInterval;
 }

}

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐