All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.unibo.scafi.simulation.SpatialSimulation.scala Maven / Gradle / Ivy

/*
 * Copyright (C) 2016-2019, Roberto Casadei, Mirko Viroli, and contributors.
 * See the LICENSE file distributed with this work for additional information regarding copyright ownership.
*/

package it.unibo.scafi.simulation

import it.unibo.scafi.config.{GridSettings, SimpleRandomSettings}
import it.unibo.scafi.platform.{SimulationPlatform, SpaceAwarePlatform}
import it.unibo.scafi.simulation.MetaActionManager.{EmptyAction, MetaAction}
import it.unibo.scafi.simulation.SimulationObserver.{MovementEvent, SensorChangedEvent}
import it.unibo.scafi.space._

import scala.collection.immutable.Queue
import scala.collection.mutable.{ArrayBuffer => MArray, Map => MMap}

trait SpatialSimulation extends Simulation with SpaceAwarePlatform  {
  self: SimulationPlatform.PlatformDependency with BasicSpatialAbstraction =>

  class DevInfo(val id: ID, var pos: P, var lsns: Map[CNAME,Any] = Map.empty, var nsns: (CNAME)=>(ID)=>Any){
    override def toString: String = s"Device[id: $id, pos: $pos]"
  }

  class SpaceAwareSimulator(
                             val space: SPACE[ID],
                             val devs: Map[ID, DevInfo],
                             toStr: NetworkSimulator => String = SpaceAwareSimulator.defaultRepr,
                             simulationSeed: Long,
                             randomSensorSeed: Long
  ) extends NetworkSimulator (
      idArray = MArray(devs.keys.toSeq:_*),
      toStr = toStr,
      simulationSeed = simulationSeed,
      randomSensorSeed = randomSensorSeed
    )  with MetaActionManager with StandardSpatialSensorNames {

    /**
      * meta action used to move a node into another position
      * @param id the node id
      * @param point the new node position
      */
    case class NodeMovement(id : ID, point : P) extends MetaAction

    /**
      * meta action used to move node using a dt movement
      * @param id the node id
      * @param dt the delta movement
      */
    case class NodeDtMovement(id : ID, dt : (Double,Double)) extends MetaAction

    /**
      * meta action used to change the value of a sensor
      * @param id the node id
      * @param sensor the sensor name
      * @param value the new value of sensor
      */
    case class NodeChangeSensor(id : ID, sensor : CNAME, value : Any) extends MetaAction

    /**
      * meta action used to move a set of node
      * @param movementMap the map of node id and new node position
      */
    case class MultiNodeMovement(movementMap : Map[ID,P]) extends MetaAction

    /**
      * a meta action used to change a set of node sensor value
      * @param ids the id of node
      * @param sensor the sensor changed
      * @param value the new sensor value
      */
    case class MultiNodeChangeSensor(ids : Set[ID], sensor : CNAME, value : Any) extends MetaAction

    private def computeAction(meta : MetaAction) : Unit = meta match {
      case NodeMovement(id,point) => this.setPosition(id,point)
      case NodeDtMovement(id,dt) => val currentPosition = this.space.getLocation(id)
        this.setPosition(id,Point3D(currentPosition.x + dt._1, currentPosition.y + dt._2, currentPosition.z).asInstanceOf[P])
      case MultiNodeMovement(map) => map.foreach {x => this.setPosition(x._1,x._2)}
      case NodeChangeSensor(id, sensor,value) => this.chgSensorValue(sensor,Set(id),value)
      case EmptyAction =>
      case _ => throw new IllegalArgumentException(s"Meta action ${meta} not supported by the spatial simulator.")
    }

    override def process(): Unit = {
      var toProcess : List[MetaAction] = List.empty
      while(actionQueue.nonEmpty) {
        toProcess = actionQueue.dequeue() :: toProcess
      }
      toProcess.foreach {_ match {
        case MetaActionManager.MultiAction(actions @ _*) => actions.foreach(computeAction(_))
        case action => computeAction(action)
      }}
    }

    override val ids: Set[ID] = devs.keySet
    override def neighbourhood(id: ID): Set[ID] = space.getNeighbors(id).toSet

    def setPosition(id: ID, newPos: P): Unit = {
      devs(id).pos = newPos
      space.setLocation(id,newPos)
      this.notify(MovementEvent(id))
    }

    def getAllNeighbours(): Map[ID, Iterable[ID]] =
      ids.iterator.map(id => id -> space.getNeighbors(id)).toMap

    override def localSensor[A](name: CNAME)(id: ID): A = devs(id).lsns(name).asInstanceOf[A]
    override def addSensor[A](name: CNAME, value: A): Unit = {
      sensors += name -> value
      chgSensorValue(name, devs.keySet, value)
    }

    override def chgSensorValue[A](name: CNAME, ids: Set[ID], value: A): Unit = {
      ids.foreach(x => {
        devs(x).lsns += name -> value
        this.notify(SensorChangedEvent(x,name))
      })
    }

    class SpatialSimulatorContextImpl(id: ID) extends SimulatorContextImpl(id) with StandardSpatialSensorNames {

      import NetworkSimulator.Optionable

      override def localSensorRetrieve[T](lsns: CNAME, id: ID): Option[T] =  devs.get(id).flatMap{ x => x.lsns.get(lsns)}.map{_.asInstanceOf[T]}

      override def nbrSensorRetrieve[T](nsns: CNAME, id: ID, nbr: ID): Option[T] =
        devs.get(id).map(_.nsns(nsns)(nbr)).map(_.asInstanceOf[T])

      override def sense[T](lsns: CNAME): Option[T] = lsns match {
        case LSNS_POSITION => space.getLocation(id).some[T]
        case _ => super.sense(lsns)
      }

      override def nbrSense[T](nsns: CNAME)(nbr: ID): Option[T] = nsns match {
        case NBR_RANGE =>
          space.getDistance(space.getLocation(selfId), space.getLocation(nbr)).some[T]
        case NBR_VECTOR => {
          val (mypos, npos) = (space.getLocation(selfId), space.getLocation(nbr))
          Point3D(npos.x-mypos.x, npos.y-mypos.y, npos.z-mypos.z).some[T]
        }
        case _ => super.nbrSense(nsns)(nbr)
      }
    }

    override def context(id: ID): CONTEXT =
      new SpatialSimulatorContextImpl(id)
  }

  object SpaceAwareSimulator {
    def defaultRepr(_net: NetworkSimulator): String = {
      val net = _net.asInstanceOf[SpaceAwareSimulator]
      net.idArray.sortBy(net.space.getLocation(_)).map {
        i => net.getExport(i).map { e => s"$i@${net.space.getLocation(i)}(${e.root()})" }.getOrElse("_")
      }.mkString("", "\t", "")
    }

    def gridRepr(numCols: Int)(_net: NetworkSimulator): String = {
      val net = _net.asInstanceOf[SpaceAwareSimulator]

      net.idArray.sortBy(net.space.getLocation(_)).map {
        i => net.getExport(i).map { e => s"$i@${net.space.getLocation(i)}(${e.root()})" }.getOrElse("_")
      }
        .zipWithIndex
        .map(z => (if (z._2 % numCols == 0) "\n" else "") + z._1)
        .mkString("", "\t", "")
    }
  }

  override def simulatorFactory: SimulatorFactory = new BasicSimulatorFactory {
    override def gridLike(gsettings: GridSettings,
                          rng: Double,
                          lsnsMap: MMap[CNAME,MMap[ID,Any]] = MMap(),
                          nsnsMap: MMap[CNAME,MMap[ID,MMap[ID,Any]]] = MMap(),
                          seeds: Seeds = Seeds(CONFIG_SEED, SIM_SEED, RANDOM_SENSOR_SEED)): NETWORK = {
      val positions = SpaceHelper.gridLocations(gsettings, seeds.configSeed)
      val ids = for(i <- 1 to gsettings.nrows * gsettings.ncols) yield i
      var lsnsById = Map[ID, Map[CNAME,Any]]()
      var nsnsById = Map[ID, Map[CNAME,Any]]()
      for(lsn <- lsnsMap.keys; (dev,v) <- lsnsMap(lsn)) {
        lsnsById += dev -> (lsnsById.getOrElse(dev, Map()) + (lsn -> v))
      }
      for(nsn <- nsnsMap.keys; (dev,v) <- nsnsMap(nsn)) {
        nsnsById += dev -> (nsnsById.getOrElse(dev, Map()) + (nsn -> v))
      }

      val devs: Map[ID,DevInfo] = ((ids map lId.fromNum) zip positions).map {
        case (id, pos) => (id, new DevInfo(id, pos.asInstanceOf[P], lsnsById.getOrElse(id, Map()), sns => nbr => nsnsById.getOrElse(id, Map())(sns)))
      }.toMap
      val space = new Basic3DSpace(devs.mapValues(v => v.pos).toMap, rng)
      new SpaceAwareSimulator(space, devs, SpaceAwareSimulator.gridRepr(gsettings.nrows), seeds.simulationSeed, seeds.randomSensorSeed)
    }

    override def basicSimulator(idArray: MArray[ID] = MArray(),
                                nbrMap: MMap[ID, Set[ID]] = MMap(),
                                lsnsMap: MMap[CNAME, MMap[ID, Any]] = MMap(),
                                nsnsMap: MMap[CNAME, MMap[ID, MMap[ID, Any]]] = MMap()): NETWORK = {
      val positions = SpaceHelper.randomLocations(SimpleRandomSettings(), idArray.length, CONFIG_SEED)

      var lsnsById = Map[ID, Map[CNAME,Any]]()
      var nsnsById = Map[ID, Map[CNAME,Any]]()
      for(lsn <- lsnsMap.keys; (dev,v) <- lsnsMap(lsn)) {
        lsnsById += dev -> (lsnsById.getOrElse(dev, Map()) + (lsn -> v))
      }
      for(nsn <- nsnsMap.keys; (dev,v) <- nsnsMap(nsn)) {
        nsnsById += dev -> (nsnsById.getOrElse(dev, Map()) + (nsn -> v))
      }

      val devs: Map[ID,DevInfo] = (idArray zip positions).map {
        case (id, pos) => (id, new DevInfo(id, pos.asInstanceOf[P], lsnsById.getOrElse(id, Map()), sns => nbr => nsnsById.getOrElse(id, Map())(sns) ))
      }.toMap
      val space = buildNewSpace(devs mapValues(v => v.pos))
      new SpaceAwareSimulator(space, devs, SpaceAwareSimulator.defaultRepr, SIM_SEED, RANDOM_SENSOR_SEED)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy