Welcome to SimFleet’s documentation!

Contents:

SimFleet

https://img.shields.io/pypi/v/simfleet.svg https://img.shields.io/travis/javipalanca/simfleet.svg Documentation Status Updates

Agent-based fleet simulator to test strategies

Features

  • Open Fleets simulator
  • Strategy pattern
  • Continuous simulator
  • Load scenarios
  • Multi-agent system built with SPADE
  • XMPP communications

Credits

This package was created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.

Installation

Stable release

To install SimFleet, run this command in your terminal:

$ pip install simfleet

This is the preferred method to install SimFleet, as it will always install the most recent stable release.

If you don’t have pip installed, this Python installation guide can guide you through the process.

From sources

The sources for SimFleet can be downloaded from the Github repo.

You can either clone the public repository:

$ git clone git://github.com/javipalanca/simfleet

Or download the tarball:

$ curl  -OL https://github.com/javipalanca/simfleet/tarball/master

Once you have a copy of the source, you can install it with:

$ python setup.py install

Quickstart

Usage

Using SimFleet is as easy as running the application in a command line. There are two use modes: a command-line interface and a graphical web-based view. You can run simulations using only the command line or using the easier and intuitive graphical user interface. Running SimFleet without your own developed strategies is posible since the application comes with a set of default strategies. Let’s explore how to use both user interfaces.

SimFleet entities summary

In SimFleet there are three types of agent that interact among them during simulations. These are the Fleet Manager agent, the Transport agent, and the Customer agent.

Description of the Customer Agents

The Customer agents represent people that need to go from one location of the city (their “current location”) to another (their “destination”) or packages that need to be moved from an origin to a destination. For doing so, each Customer agent requests a single transport service and, once it is transported to its destination, it reaches its final state and ends its execution.

Description of the Transport Agent

The Transport agents represent vehicles which can transport Customer agents from their current positions to their respective destinations.

Description of the FleetManager Agent

The FleetManager Agent is responsible for putting in contact the Customer agents that need a transport service, and the Transport agents that may be available to offer these services. In short, the FleetManager Agent acts like a transport call center, accepting the incoming requests from customers (Customer agents) and forwarding these requests to the (appropriate) Transport agents. In order to do so, the FleetManager has a registration protocol by which Transport agents subscribe to the Fleet Manager that represents their fleet. This is automatically done when a Transport agent is started.

In the context of SimFleet, a “transport service” involves, once a particular Customer and Transport agents have reached an agreement, the movement of the Transport agent from its current position to the Customer’s position in order to pick the Customer up, and then the transportation of the Customer agent to its destination.

Command-line interface

After installing SimFleet open a command-line and type simfleet --config config_file.json. This starts a simulator with the configuration specified at the JSON file and runs the simulator agent. The console will output the default logging information and you can terminate the simulator by pressing Ctrl+C. When you terminate the simulator the results of the simulations are printed.

Hint

To install an XMPP server visit https://xmpp.org/software/servers.html (we recommend Prosody IM)

$ simfleet --config myconfig.json
2015-10-21 16:29:07.049 | INFO     | simfleet.config:load_config:75 - Reading config myconfig.json
2015-10-21 16:29:07.062 | INFO     | simfleet.simulator:__init__:71 - Starting SimFleet (SimFleet)
2015-10-21 16:29:07.064 | INFO     | simfleet.simulator:load_icons:172 - Reading icons
2015-10-21 16:29:07.158 | INFO     | simfleet.directory:setup:40 - Directory agent running
2015-10-21 16:29:07.159 | INFO     | simfleet.simulator:__init__:91 - Creating 0 managers, 0 transports, 0 customers and 0 stations.
2015-10-21 16:29:07.159 | INFO     | simfleet.simulator:load_scenario:116 - Loading scenario...
2015-10-21 16:29:07.162 | INFO     | simfleet.route:setup:28 - Route agent running
2015-10-21 16:29:07.162 | WARNING  | simfleet.route:load_cache:74 - Could not load cache file.
2015-10-21 16:29:07.226 | INFO     | simfleet.simulator:setup:97 - Simulator agent running
2015-10-21 16:29:07.229 | INFO     | simfleet.simulator:setup:110 - Web interface running at http://127.0.0.1:9000/app

^C

2015-10-21 16:29:21.292 | INFO     | simfleet.simulator:stop:258 -
Terminating... (0.0 seconds elapsed)
Simulation Results
+===================+====================+==================+===================+============+=======================+
| Simulation Name   |   Avg Waiting Time |   Avg Total Time |   Simulation Time |   Max Time | Simulation Finished   |
+===================+====================+==================+===================+============+=======================╡
| SimFleet          |                  0 |                0 |                 0 |       1000 | False                 |
+===================+====================+==================+===================+============+=======================+
Fleet Manager stats
+==============+=======================+========+
| fleet_name   | transports_in_fleet   | type   |
+==============+=======================+========╡
+==============+=======================+========+
Customer stats
+========+================+==============+==========+
| name   | waiting_time   | total_time   | status   |
+========+================+==============+==========╡
+========+================+==============+==========+
Transport stats
+========+===============+============+==========+
| name   | assignments   | distance   | status   |
+========+===============+============+==========╡
+========+===============+============+==========+
Station stats
+========+==========+====================+=========+
| name   | status   | available_places   | power   |
+========+==========+====================+=========╡
+========+==========+====================+=========+
2015-10-21 16:29:21.360 | INFO     | simfleet.simulator:stop:258 -
Terminating... (0.0 seconds elapsed)
Simulation Results
+===================+====================+==================+===================+============+=======================+
| Simulation Name   |   Avg Waiting Time |   Avg Total Time |   Simulation Time |   Max Time | Simulation Finished   |
+===================+====================+==================+===================+============+=======================╡
| SimFleet          |                  0 |                0 |                 0 |       1000 | False                 |
+===================+====================+==================+===================+============+=======================+
Manager stats
+==============+=======================+========+
| fleet_name   | transports_in_fleet   | type   |
+==============+=======================+========╡
+==============+=======================+========+
Customer stats
+========+================+==============+==========+
| name   | waiting_time   | total_time   | status   |
+========+================+==============+==========╡
+========+================+==============+==========+
Transport stats
+========+===============+============+==========+
| name   | assignments   | distance   | status   |
+========+===============+============+==========╡
+========+===============+============+==========+
Station stats
+========+==========+====================+=========+
| name   | status   | available_places   | power   |
+========+==========+====================+=========╡
+========+==========+====================+=========+

However, if you don’t use some options when running the simulator there will be no default transports nor customers. That’s why stats are empty. To run a simulation with some parameters you must fill a configuration file where the simulation scenario is defined.

To show he command line interface options you can enter the --help command:

  $ simfleet --help

  Usage: simfleet [OPTIONS]

Console script for SimFleet.

  Options:
    -n, --name TEXT              Name of the simulation execution.
    -o, --output TEXT            Filename to save simulation results.
    -of, --oformat [json|excel]  Output format used to save simulation results.
                                 (default: json)
    -mt, --max-time INTEGER      Maximum simulation time (in seconds).
    -r, --autorun                Run simulation as soon as the agents are ready.
    -c, --config TEXT            Filename of JSON file with initial config.
    -v, --verbose                Show verbose debug level: -v level 1, -vv level
                                 2, -vvv level 3, -vvvv level 4
    --help                       Show this message and exit.

The output of a simulation shows some statistics of the simulation, with the Average Total Time, which represents the average time of customers from the moment they request a transport until they are delivered to their destination, and the Average Waiting Time, which is the average time of customers from requesting a transport to being picked up. This information is also shown for each customer along with their status at the end of the simulation.

In the case of transports, the shown information includes the number of assignments of each transport (how many customers it has delivered), the total distance it has traveled and its final status.

This information is going to be useful for the development of new strategies that improve the system balancing or for debugging errors if a transport or a customer gets stuck or any other unexpected situation occurs.

The last but no less important option is the verbosity option. It allows you to specify how verbose you want the simulator to be. The number of v letters you pass to the option indicates the level of verbosity (e.g. -v is DEBUG verbosity and -vvvv is the highest level of verbosity where the internal messages of the platform are shown).

The Config file: Loading Scenarios

The ability to load scenarios to SimFleet allows us to repeat the same experiment as many times as we want with the same initial conditions. SimFleet supports to load a config file that defines all the fields that you need to load the same information repeatedly. A scenario file must be coded in JSON format.

The most important fields that the scenario file must include are a customers list and a transports list. Each customer must include the following fields:

Customers
Field Description
position Initial coordinates of the customer
destination Destination coordinates of the customer
name Name of the customer
password Password for registering the customer in the platform (optional)
fleet_type Fleet type that the customer wants to use
icon Custom icon (in base64 format) to be used by the customer (optional)
strategy Custom strategy file in the format module.file.Class (optional)

For transports the fields are as follows:

Transports
Field Description
position Initial coordinates of the transport
name Name of the transport
password Password for registering the transport in the platform (optional)
speed Speed of the transport (in meters per second) (optional)
fleet_type Fleet type that the customer wants to use
fleet The fleet manager’s JID to be subscribed to (optional)
autonomy The maximum autonomy of the transport (in km) (optional)
current_autonomy The initial autonomy of the transport (in km) (optional)
icon Custom icon (in base64 format) to be used by the transport (optional)
strategy Custom strategy file in the format module.file.Class (optional)

For fleet managers the fields are as follows:

Fleet managers
Field Description
position Initial coordinates of the manager
name Name of the manager
password Password for registering the manager in the platform (optional)
fleet_type Fleet type that the agent manages
icon Custom icon (in base64 format) to be used by the manager (optional)
strategy Custom strategy file in the format module.file.Class (optional)

An example of a config file with two customers, two transports and one fleet manager:

{
    "fleets": [
        {
            "password": "secret",
            "name": "fleetm1",
            "fleet_type": "drone"
        },
        {
            "password": "secret",
            "name": "fleetm3",
            "fleet_type": "food_delivery"
        },
        {
            "password": "secret",
            "name": "fleetm2",
            "fleet_type": "drone"
        }
    ],
    "transports": [
        {
            "speed": 2000,
            "fleet": "fleetm1@localhost",
            "fleet_type": "drone",
            "position": [40.41192762537371, -3.7105464935302734],
            "password": "secret",
            "name": "drone1"
        },
        {
            "speed": 2000,
            "fleet": "fleetm1@localhost",
            "fleet_type": "drone",
            "position": [40.428655600133546,-3.6993885040283203],
            "password": "secret",
            "name": "drone2"
        },
        {
            "speed": 2000,
            "fleet": "fleetm2@localhost",
            "fleet_type": "drone",
            "position": [40.446424515534666, -3.6612796783447266],
            "password": "secret",
            "name": "drone3"
        },
        {
            "speed": 2000,
            "fleet": "fleetm3@localhost",
            "fleet_type": "food_delivery",
            "position": [40.44635919724081,-3.69140625],
            "password": "secret",
            "name": "bike1"
        },
        {
            "speed": 2000,
            "fleet": "fleetm3@localhost",
            "fleet_type": "food_delivery",
            "position": [40.42035747630869,-3.665142059326172],
            "password": "secret",
            "name": "bike2"
        }
    ],
    "customers": [
        {
            "destination": [40.446163241978304,-3.7075424194335938],
            "position": [40.45171508652634,-3.677501678466797],
            "password": "secret",
            "name": "c1",
            "fleet_type": "drone"
        },
        {
            "destination": [40.4068299938421,-3.670291900634765],
            "position": [40.43087697137461,-3.716297149658203],
            "password": "secret",
            "name": "c2",
            "fleet_type": "drone"
        },
        {
            "destination": [40.43002763221108,-3.6797332763671875],
            "position": [40.45759301026131,-3.664026260375976],
            "password": "secret",
            "name": "c3",
            "fleet_type": "drone"
        },
        {
            "destination": [40.45785423938172,-3.711318969726563],
            "position": [40.440088345478614,-3.680849075317383],
            "password": "secret",
            "name": "f1",
            "fleet_type": "food_delivery"
        },
        {
            "destination": [40.458572614225545,-3.680419921875],
            "position": [40.409770982232956,-3.6928653717041016],
            "password": "secret",
            "name": "f2",
            "fleet_type": "food_delivery"
        }
    ],
    "stations": [
        {
            "name": "station1",
            "password": "secret",
            "position": [40.424559,-3.7002277],
            "places": 2,
            "power": 50,
            "icon": "gas_station"
        }
    ],
    "simulation_name": "Example Config",
    "max_time": 1000,
    "verbose": 1,
    "transport_strategy": "simfleet.strategies.AcceptAlwaysStrategyBehaviour",
    "customer_strategy": "simfleet.strategies.AcceptFirstRequestBehaviour",
    "fleetmanager_strategy": "simfleet.strategies.DelegateRequestBehaviour",
    "directory_strategy": "simfleet.directory.DirectoryStrategyBehaviour",
    "station_strategy": "simfleet.station.StationStrategyBehaviour",
    "fleetmanager_name": "fleetmanager",
    "fleetmanager_password": "fleetmanager_passwd",
    "route_name": "route",
    "route_password": "route_passwd",
    "directory_name": "directory",
    "directory_password": "directory_passwd",
    "host": "localhost",
    "xmpp_port": 5222,
    "http_port": 9000,
    "http_ip": "127.0.0.1",
    "coords": [40.4167754, -3.7037902],
    "zoom": 14
}

The rest of configuration parameters are referred to general settings of the simulator such as coords and zoom which allows the user to set up the coordinates and zoom of the city where the simulation is run.

If you want to store the results of simulation in a file you may use the --output option (or -o) to specify the name of the file where the simulation results will be saved. The --oformat (-of) allows you to choose the output format between json (default) or excel. It is also useful to use the --name (or -n) to name the simulation.

Example:

$ simfleet --config myconfig.json --name "My Simulation" --output results.xls --oformat excel

Graphical User Interface

A much more user-friendly way to use SimFleet is through the built-in graphical user interface. This interface is accessed via any web browser and is designed as a viewer for your running simulations. To open it just visit the address shown on the screen when you run the simulator and access that website.

Hint

The Simulator agent is who raises the GUI and shows the address in the console output:

2015-10-21 16:29:07.229 | INFO     | simfleet.simulator:setup:110 - Web interface running at http://127.0.0.1:9000/app

This address is (in most cases): http://127.0.0.1:9000/app

Once you visit the GUI address you see an interface like this:

GUI at startup

GUI at startup

In the GUI you can see a map of the city on the right and a Control Panel with various options on the left:

  1. A Run button that starts the simulation.
  2. A Clear button to stop and reset the simulation.
  3. Stats of the waiting time and total time of the simulation in real time.
  4. A Download button to get the stats of the simulation in excel or json format.
  5. A collapsable tree view with the transports and customers that are included in the simulation, with a color bullet that indicates their current status.

If the Run button is pressed the simulation shows how the transports move to the customers and deliver them to their destinations.

Simulation in progress

Simulation in progress

Notice that when a transport picks up a customer, the customer’s icon disappears from the map view (since it is inside the transport) and is no longer viewed (it’s also not shown when it arrives to its destination). However, you can check at any time your customers status in the tree view of the Control Panel.

The code colors in the tree view indicate the status of a transport or a customer. The legend of colors is as follows:

Transports Customers
Bullet Status Bullet Status
positive WAITING active WAITING
inter WAITING FOR APPROVAL inter ASSIGNED
interpulse MOVING TO CUSTOMER activepulse IN TRANSPORT
activepulse MOVING TO DESTINATION positive IN DESTINATION

Hint

Every time than a bullet is pulsing means that the agent is moving.

When a transport is moving it’s also shown in the GUI the path that the transport is following. The color of the path indicates the type of movement that the transport is doing. A yellow path indicates that the transport is going to pick up the customer. On the other hand, a blue path indicates that the transport is taking the customer to his destination.

Note

A simulation is finished when all transports are free (and waiting for new customers) and all customers are in their destinations (i.e. all bullets are green).

Developing New Strategies

Introduction

One of the main features of “SimFleet” is the ability to change the default negotiation strategy of the agents that interact during the simulation: the Fleet Manager agents, the Transport agents and the Customer agents. The overall goal of the negotiation strategy of these three agent types is to decide which Transport agent will transport each Customer agent to its destination, making sure that no Customer agent is left unattended. Additionally, the negotiation strategy may also try to optimize some metrics, such as the average time that Customer agents are waiting to be served, or that the amount of gas spent by Transport in their movements.

The negotiation strategy is based on two main elements. First, it is based on the internal logic of each agent type (FleetManager, Transport and Customer) and, in particular, on their respective strategy behaviour, which includes the internal logic of each agent type regarding the negotiation process. And second, it is also based on the so-called REQUEST protocol, which comprises the types of messages exchanged among the three agent types during the negotiation. The following diagram presents the protocol in the typical FIPA format, where agents types are depicted as vertical lines and the exchanged message types (or “performatives”) in horizontal arrows:

_images/protocol_v3.png

This chapter introduces first the current, default strategy of each agent type (FleetManager, Transport and Customer) and then explains how to introduce new strategies for any, or all, of them.

Fleet Manager Strategy Behaviour (DelegateRequestBehaviour)

The FleetManager Agent includes a single behaviour, which is its strategy behaviour, now described. The goal of the stategy behaviour of the FleetManager Agent is basically to receive the “request” messages (REQUEST_PERFORMATIVE) sent by the Customer agents that need a transport service and, for each request, selecting the Transport agent, or agents, that may perform the service, and forward the request to them. A REQUEST_PERFORMATIVE message includes the following fields:

"customer_id":  Id of the Customer agent that performs the request.
"origin":       Current position of the Customer, where the Transport has to pick it up.
"dest":         Destination of the Customer, where the Transport needs to transport it.

The particular set of Transport agents to which the request will be forwarded depends on the allocation policy of the FleetManager Agent, which is part of the strategy. In the default strategy behaviour for the FleetManager agent (DelegateRequestBehaviour), the allocation policy is the simplest posible: it forwards every incoming request to all the Transport agents that are registered in its fleet, regardless of their current statuses or any other consideration (such as, for example, the last time they performed a service, or the distance between them and the Customer agent).

In the default strategy behaviour, the set of incoming messages that may be delivered to the FleetManager Agent is reduced to the requests made by Customer agents, and the behaviour itself does not include multiple states. So, each incoming message is processed in the same way, and leaves the behaviour in the same (unique) state.

Once each request has been forwarded to some (or all) the Transport agents, the goal of the FleetManager Agent for that request is achieved. This is the starting point to the negotiation between the Customer that has issued the request and the Transport agents that have received it, which is described in the following sections.

Transport Agents Behaviours

Transport agents incorporate two behaviours: the strategy behaviour and the moving behaviour, now described.

Strategy Behaviour (AcceptAlwaysStrategyBehaviour)

The goal of the strategy behaviour of a Transport agent is to negotiate with Customer agents which are requesting a transport service the conditions of the service offered by the Transport, in order to achieve an agreement with these Customer agents. When an agreement is reached between a particular Customer and Transport agents, then the Transport agent picks up the Customer agent and transport it to its destination (and starts the Moving behaviour, described below).

The currently implemented, default strategy behaviour is called AcceptAlwaysStrategyBehaviour, and has a direct relation with the REQUEST protocol explained above. In particular, the behaviour can be thought of as a finite-state machine with some different states specifying the statuses of the Transport agent regarding the strategy behaviour, and some transitions between states, wich are triggered either by messages (of the REQUEST protocol) received by the Transport agent, or by some other program conditions. This is depicted in the following diagram:

_images/SimFleet_FSM.png

States and transitions of the strategy behaviour of a Transport agent.

The semantics of each state are now described:

  • TRANSPORT_WAITING: In this state, the Transport agent is available (free) and waiting for requests from Customer agents. While in this state, if it receives a request message (REQUEST_PERFORMATIVE) from a particular Customer agent, it will send the Customer a service proposal (PROPOSE_PERFORMATIVE) and it will change its state to TRANSPORT_WAITING_FOR_APPROVAL.
  • TRANSPORT_WAITING_FOR_APPROVAL: In this state, the Transport agent is waiting for the response message from a Customer agent to which it has sent a service proposal message. While in this state, it may receive two alternative answers from the Customer agent: (1) the Customer refuses the service proposal (REFUSE_PERFORMATIVE), in which case the Transport changes its state back to TRANSPORT_WAITING; or (2) the Customer accepts the proposal (ACCEPT_PERFORMATIVE), in which case it will change to the state TRANSPORT_MOVING_TO_CUSTOMER.
  • TRANSPORT_MOVING_TO_CUSTOMER: In this state, the Transport agent and the Customer agent have agreed to perform a transport service, and then the Transport agent starts to travel to the Customer location in order to pick it up. This is the final state of the negotiation between the Transport and a certain Customer agent. When the Transport agent arrives to the Customer’s position, the Transport agent executes the helper function pick_up_customer, which automatically starts the so-called Moving behaviour in the Transport agent, described below. It also sends a message to the Travel behaviour of the Customer agent, which starts that behaviour (this is explained in the next section).
Moving Behaviour

This behaviour makes the Transport agent to move to the current location of the Customer agent with which it has reached an agreement to perform a transport service. After picking the Customer agent up, the Transport will then transport it to its destination. During that travel, the behaviour informs the Customer agent of where the Transport is and what it is doing (going to pick up the Customer, taking the Customer to its destination, reaching the destination, etc.). All this is performed by sending the Customer agent some messages which belong of another, dedicated protocol called TRAVEL_PROTOCOL.

Once the Transport reaches the Customer agent’s destination and the Customer agent is informed about it, the state of the Transport agent (of the strategy behaviour) is here changed to TRANSPORT_WAITING, indicating that it is now free, and hence making the Transport agent available again to receiving new requests from other Customer agents.

Warning

This behaviour is internal and automatic, and it is not intended to be modified while developing new negotiation strategies. The same applies to the TRAVEL_PROTOCOL protocol.

Customer Agents Behaviours

Customer agents incorporate two behaviours: the strategy behaviour and the travel behaviour, now described.

Strategy Behaviour

In the course of the REQUEST protocol, the request of a transport service made by a Customer agent is answered by one (or several) Transport agents, each of which offering the Customer their conditions to perform such service. The goal of the strategy behaviour of a Customer agent is to select the best of these transport service proposals, according to its needs and/or preferences (e.g., to be picked up faster, to get the nearest available transport, to get the cheapest service, etc.).

The currently implemented default strategy behaviour is called AcceptFirstRequestBehaviour. As in the strategy behaviour of the Transport agents above, here we can also consider the strategy as a finite-state machine related to the messages (of the REQUEST protocol) received by the Customer agent, as depicted below:

_images/Customer_FSM.png

States and transitions of the strategy behaviour of a Customer agent.

The semantics of each state are now described:

  • CUSTOMER_WAITING: In this state, the Customer agent requires a transport service and, periodically, sends a request for that service until one (or many) Transport agent proposals (PROPOSE_PERFORMATIVE) are received. When the Customer accepts a particular proposal (in the current implementation, always the first one it receives while in this state) then it communicates so to the proposing Transport agent, and changes its own status to CUSTOMER_ASSIGNED.
  • CUSTOMER_ASSIGNED: In this state, the Customer agent has been assigned to a particular transport, and the transport service is being performed. The Customer side of the transport service is implemented by activating the Travel behaviour, described below, which is started by a message sent by the Transport agent (in its helper function pick_up_customer). If something goes wrong (for example, an exception is raised during the transport service) or the Transport agent voluntarily wants to cancel the service, then the Transport agent sends a CANCEL_PERFORMATIVE to the Customer agent, which would then change its status back to CUSTOMER_WAITING, initiating the request process again.
Travel Behaviour

This behaviour is activated (in the Customer agent) when a Transport agent decides to pick up the Customer agent, by means of a message sent by the Transport (inside the Transport agent’s helper function pick_up_customer). This message, as well as other messages sent by the Transport agent to this behaviour, belongs to a protocol called TRAVEL_PROTOCOL.

The messages of the TRAVEL_PROTOCOL drive the transitions between the different states of this behaviour, in the same way that the REQUEST_PROTOCOL does for the strategy behaviour. In particular, the states of this behaviour are: CUSTOMER_IN_TRANSPORT, when the Transport agent has reached the Customer agent’s position and has picked it up; and CUSTOMER_IN_DEST, when the Transport agent has reached the Customer agent’s destination. This would be the final state of the Customer agent.

Warning

This behaviour is internal and automatic, and it is not intended to be modified while developing new negotiation strategies. The same applies to the TRAVEL_PROTOCOL protocol.

The Negotiation Process between Transport and Customer Agents

After separately explaining the strategy behaviour of Transport and Customer agents, this section tries to relate both behaviours. This is important to understand how these two agent types interact with each other in order to coordinate and reach the overall goals of the simulation.

In particular, there are three key aspects (embedded within the strategy behaviours) which influence the overall coordination process implemented in the simulator, as now described:

  • The conditions of a transport service proposal. The current implementation does not consider any special condition other than the Transport agent being free (available to perform the service). Some aspects that could be included in a transport proposal would be, for example, the current location of the transport, the proposed fare, the route to take the Customer agent to its destination, etc.
  • The preferences of customers in order to select a particular transport proposal. In the current implementation, the Customer agents always accept the first proposal received from a Transport agent. In a more sophisticated negotiation, some internal goals/conditions of the Customer agent could be taken into account in order to select a “better” proposal. These might include, for example, the expected waiting time until the Transport agent arrives, the amount of money that the service is expected to cost, the brand of the Transport vehicle, etc.
  • The possibility of a transport to voluntarily cancel an ongoing transport service after a proposal has been accepted by a customer. This may happen only before the customer has been picked up, that is, while the transport is moving from its initial position to the location where the customer is waiting for it. In the current implementation, a transport service cancellation can only be produced if some exception is raised while the service is being produced (for example, if the software calculating a route for the Transport agent fails to produce a valid route). Since new Customer (and maybe Transport) agents can appear at any time while the simulation is running, a voluntary cancellation of transport services could improve the overall transportation of customers throughout the simulation, allowing for a “dynamic reallocation” of customers to transports, even when transport services were already committed.

Agent Foundations

The architecture of SimFleet is built on top of a multi-agent system platform called SPADE. Although it is not necessary to build new agents in order to develop new coordination strategies (the simulator provides all the necessary agents), it is interesting to know how they work and what methods they provide for the creation of coordination strategies.

Next we will present the SPADE platform and its main features. For more documentation you can visit their website https://github.com/javipalanca/spade.

SPADE

SPADE (Smart Python multi-Agent Development Environment) is a multi-agent system (MAS) platform based on the XMPP technology and written in the Python programming language. This technology offers by itself many features and facilities that ease the construction of MAS, such as an existing communication channel, the concepts of users (agents) and servers (platforms) and an extensible communication protocol based on XML.

Extensible Messaging and Presence Protocol (XMPP) is an open, XML-inspired protocol for near-real-time, extensible instant messaging (IM) and presence information. The protocol is built to be open and free, asynchronous, decentralized, secure, extensible and flexible. The latter two features allow XMPP not only to be an instant messaging protocol, but also to be extended and used for many tasks and situations (IoT, WebRTC, social, …). SPADE itself uses some XMPP extensions to provide extended features to its agents, such as remote procedure calls between agents (Jabber-RPC), file transfer (In-Band Bytestreams), an so on.

In order to fully understand how SPADE works, it is necessary to know how the agents are made up and how they communicate. In the following sections we will summarize the SPADE agent model and its communication API.

Agent Model: Behaviours

SPADE agents are threaded-based objects that can be run concurrently and that are connected to a SPADE platform, which internally runs an XMPP server. Each agent must provide an ID and password in order to be allowed to connect to the platform. The agent ID is called JID and has the form of an email: a user name string plus a “@” character plus the IP address of the SPADE server to connect to (e.g. my_agent@127.0.0.1).

The internal components of the SPADE agents that provide their intelligence are the Behaviours. A behaviour is a task that an agent can run using some pre-defined repeating pattern. For example, the most basic behaviour type (pattern) is the so-called cyclic behaviour, which repeatedly executes the same method over and over again, indefinitely. This is the way to develop typical behaviours that wait for a perception, reason about it and finally execute an action, and then wait for the next perception.

The following example is a sample of an agent with a cyclic behaviour (spade.behaviour.CyclicBehaviour type) that waits for a perception from the keyboard input, reasons on it and executes an action, and continues to do so indefinitely until the user presses Ctrl+C. In order to build a behaviour, you need to inherit from the type of behaviour you want (in the case of this example, the cyclic behaviour is implemented in the class spade.behaviour.CyclicBehaviour) and overload the coroutine run where the body of the behaviour is implemented. If needed, you can also overload the on_start and on_end coroutines in order to execute actions on the initialization or shutdown of a behaviour, respectively.

import spade
import datetime
import time

class MyAgent(spade.agent.Agent):
    class MyBehaviour(spade.behaviour.CyclicBehaviour):

        async def on_start(self):
            print("Initialization of behaviour")

        async def run(self):
            # wait for perception, raw_input is a blocking call
            perception = raw_input("What's your birthday year?")
            # reason about the perception
            age = datetime.datetime.now().year - perception
            # execute an action
            print("You are {age} years old.".format(age=age))

        async def on_end(self):
            print("Shutdown of behaviour")

    async def setup(self):
        # Create behaviour
        behaviour = self.MyBehaviour()
        # Register behaviour in agent
        self.add_behaviour(behaviour)

if __name__ == "__main__":
    a = MyAgent(jid="agent@127.0.0.1", password="secret")
    a.start()
    while True:
        try:
            time.sleep(1)
        except KeyboardInterrupt:
            break
    a.stop()

Along with the cyclic repeating pattern (or type), SPADE also provides several other types of behaviours, such as like one-shot behaviours, periodic behaviours, finite-state machine behaviours, etc. It is important to note that SPADE agents can execute many behaviours simultaneously, from the same or different types.

Communication API, Messages and Templates

Communication is one of the cornerstones of any multi-agent system, and SPADE is no exception. Agents can send and receive messages using a simple API, and more importantly, they can receive them in certain behaviours according to templates they can define.

A spade.message.Message is the class that needs to be filled in order to send a message. A Message may be filled with several pieces of information, but the most important fields are the receiver, the content, the performative and the protocol. The receiver must be filled with a jid address , which is a string. The content is the (string-based) body of the message. The performative and protocol both add semantic information to the message in the context of a conversation: they are normally used to represent the action and the rules that determine how the agents are going to communicate in a specific semantic context and they are represented as metadata.

Tip

It is usually recommended to use a representation language for the content of the message. Although semantic languages like OWL or RDF are normally used for this purpose, in this simulator JSON is used instead, for the sake of simplicity.

All these fields have a getter and setter function. An example is shown next:

import spade

msg = spade.message.Message()
msg.to = "receiver_agent@127.0.0.1"
msg.set_metadata("performative", "request")
msg.set_metadata("protocol", "my_custom_protocol")
msg.body = "{'a_key': 'a_value'}"

Hint

Other metadata fields that can be filled in the message are the content language, the ontology, and so on.

The next step is to send the message. This is done with the send coroutine provided by a Behaviour. For example:

import spade


class SenderAgent(spade.agent.Agent):
    class SendBehav(spade.behaviour.OneShotBehaviour):

        async def run(self):
            msg = spade.message.Message()
            msg.to = "receiver@127.0.0.1"
            msg.set_metadata("performative", "inform")
            msg.set_metadata("ontology", "myOntology")
            msg.set_metadata("language", "OWL-S")
            msg.body = "Hello World"

            await self.send(msg)  # send the message

    async def setup(self):
        print "MyAgent starting..."
        behav = self.SendBehav()
        self.add_behaviour(behav)

The reception of messages is particular in SPADE, since messages can only be received by behaviours, and so SPADE provides each behaviour executed by any agent with its own mailbox, and defines a mechanism in order to configure the particular behaviour that must receive each message, according to the message type. This mechanism is carried out with Templates. When an agent receives a new message it checks if the message matches each of the behaviours using a template with which they where registered. If there is a match, the message is delivered to the mailbox of the corresponding behaviour, and will be read when the behaviour executes the receive method. Otherwise, the message will be dropped.

Note

The receive coroutine accepts an optional parameter: timeout=seconds, which allows the coroutine to be blocking until the specified number of seconds have elapsed. If the timeout is reached without a message being received, then None is returned. If the timeout is set to 0, then the receive() function is non-blocking and (immediately) returns either a spade.message.Message or None.

A spade.template.Template is created using the same API of spade.message.Message:

import spade
template = spade.template.Template()
template.set_metadata("ontology", "myOntology")

Note

A spade.template.Template accepts boolean operators to combine Templates (e.g. my_tpl = Template( template1 & template2))

At this point we can present a full example on how to build an agent that registers a behaviour with a template and receives messages that match that template:

import spade
import asyncio

class RecvAgent(spade.agent.Agent):
    class ReceiveBehav(spade.behaviour.CyclicBehaviour):

        async def run(self):
            await msg = self.receive(timeout=10)

            # Check wether the message arrived
            if msg is not None:
                assert "myOntology" == msg.get_metadata("ontology")
                print("I got a message with the ontology 'myOntology'")
            else:
                print("I waited 10 seconds but got no message")

    async def setup(self):
        recv_behav = self.ReceiveBehav()
        template = spade.template.Template()
        template.set_metadata("ontology", "myOntology")

        self.add_behaviour(recv_behav, template)

These are the basics of SPADE programming. You will not need to create all these structures, templates and classes in order to use SimFleet, but it is always better to know the foundations before getting down to business.

How to Implement your own Strategies

SimFleet is designed for users to implement and test new strategies that lead to system optimization. The goal of this simulator is to make it easier for users to work with new coordination strategies without having to introduce major modifications to the application. For this purpose, SimFleet incorporates the so-called Strategy design pattern, which is now introduced.

The Strategy Pattern

The Strategy Pattern is a design pattern that enables selecting an algorithm at runtime. The Strategy Pattern is the best practice when an application incorporates different, alternative versions of an algorithm and we want to be able to select any of these versions to be executed at run time. With this pattern, you can define a separate strategy (implementation of the algorithm) in an object that encapsulates the algorithm. The application that executes the algorithm must define an interface that every strategy (implementation) will follow, as it can be seen in the following figure:

The Strategy Pattern UML

The Strategy Pattern UML.

Following this implementation, the context object can call the current strategy implementation without knowing how the algorithm was implemented. This design pattern was created, among others, by a group of authors commonly known as the Gang of Four (E. Gamma, R. Helm, R. Johnson and J. Vlissides), and it is well presented in [GangOfFour95].

SimFleet uses the Strategy Pattern in order to enable users to implement three different strategies (one for the fleet manager agent, one for the transport agent and one for the customer agent) without having to develop new agents or entering in the complexity of the simulator. Thanks to this pattern, users can develop their strategies in an external file and pass it as an argument when the simulator is run.

SimFleet implements one interface for each of these three agents, with each interface also providing some helper functions that intend to facilitate the most common actions of each (subclassed) agent. These three interfaces inherit from the StrategyBehaviour class and are called: FleetManagerStrategyBehaviour, TransportStrategyBehaviour and CustomerStrategyBehaviour.

The StrategyBehaviour class and their inherited interfaces

The StrategyBehaviour class and their inherited interfaces.

The Strategy Behaviour

The StrategyBehaviour is the metaclass from which interfaces are created for the strategies of each agent in the simulator. It inherits from a spade.behaviour.CyclicBehaviour class, so when implementing it, you will have to overload the run coroutine that will run cyclically (and endlessly), until the agent stops.

Helpers

The Strategy Behaviour provides also some helper functions that are useful in general for any kind of agent in the simulator.

Danger

Don’t store information in the Behaviour itself since it is a cyclic behaviour and is run by calling repeteadly the run coroutine, so the context of the function is not persistent. Use the agent variable that is accesible from any behaviour as self.agent. (i.e. you can do self.agent.set("my_key", "my_value") and self.agent.get("my_key").

The set and get functions allow to store persistent information in the agent and to recover it at any moment. The store uses a key-value interface to store custom-defined data.

There is also a very useful helper function which is the logger. This is not a single function but a system of logs which can be used to generate debug information at different levels. There are five levels of logging which are now presented, in order of importance:

  • DEBUG
    Used with logger.debug("my debug message"). These messages are only shown when the simulator is called with the -v option. This is usually superfluous information.
  • INFO
    Used with logger.info("my info message"). These messages are always shown and are the regular information shown in logs.
  • WARNING
    Used with logger.warn("my warning message"). These messages are always shown and are used to show warnings to the user.
  • ERROR
    Used with logger.error("my error message"). These messages are always shown and are used to show errors to the user.
  • SUCCESS
    Used with logger.success("my success message"). These messages are always shown and are used to show success messages to the user.

In order to use this logger just remember to import the loguru library as follows:

from loguru import logger

Developing the FleetManager Agent Strategy

In order to develop a new strategy for the FleetManager Agent, you need to create a class that inherits from FleetManagerStrategyBehaviour. Since this is a cyclic behaviour class that follows the Strategy Pattern and that inherits from the StrategyBehaviour, it has all the previously presented helper functions for communication and storing data inside the agent.

Following the REQUEST protocol, the FleetManager agent is supposed to receive every request for a transport service from customers and to carry out the action that your strategy determines (note that, in the default strategy DelegateRequestBehaviour, the fleet manager delegates the decision to the transports themselves by redirecting all requests to all their registered transports without any previous, additional reasoning). The code of the DelegateRequestBehaviour is presented below.

The place in the code where your fleet manager strategy must be coded is the run coroutine. This function is executed in an infinite loop until the agent stops. In addition, you may also overload the on_start and the on_end coroutines, in order to execute code before the creation of the strategy or after its destruction, if needed.

Code

This is the code of the default fleet manager strategy DelegateRequestBehaviour:

from simfleet.fleetmanager import FleetManagerStrategyBehaviour

async def run(self):
    if not self.agent.registration:
        # Register into Directory Agent to make your fleet public
        await self.send_registration()

    msg = await self.receive(timeout=5)
    logger.debug("Manager received message: {}".format(msg))
    if msg:
        # Redirect request to all your registered transports
        for transport in self.get_transport_agents().values():
            msg.to = str(transport["jid"])
            logger.debug("Manager sent request to transport {}".format(transport["name"]))
            await self.send(msg)
Helpers

The fleet manager agent incorporates two helper functions:

  • send_registration

    Registers its fleet in the Directory agent. This way customers can find their fleet and request for services.

  • get_transport_agents

    Returns a list of the transports that are registered in that fleet.

Developing the Transport Agent Strategy

To develop a new strategy for the Transport Agent, you need to create a class that inherits from TransportStrategyBehaviour. Since this is a cyclic behaviour class that follows the Strategy Pattern and that inherits from the StrategyBehaviour, it has all the previously presented helper functions for communication and storing data inside the agent.

The transport strategy is intended to receive requests from customers, forwarded by its fleet manager agent, and then to send proposals to these customers in order to be selected by the corresponding customer. If a transport proposal is accepted, then the transport begins the process of going to the customer’s current position, picking the customer up, and taking the customer to the requested destination.

Warning

The process that implies a transport movement is out of the scope of the strategy and should not be addressed by the strategy implementation. This customer-transfer process is automatically triggered when the strategy executes the helper coroutine pick_up_customer (which is supposed to be the last action of a transport strategy).

The place in the code where your transport strategy must be coded is the run coroutine. This function is executed in an infinite loop until the agent stops. In addition, you may also overload the on_start and the on_end coroutines, in order to execute code before the creation of the strategy or after its destruction, if needed.

Code

The default strategy of a transport is to accept any customers’ request if the transport is not assigned to any other customer or waiting a confirmation from any customer. This is the code of the default transport strategy AcceptAlwaysStrategyBehaviour:

from simfleet.transport import TransportStrategyBehaviour

class AcceptAlwaysStrategyBehaviour(TransportStrategyBehaviour):
    async def run(self):
        if self.agent.needs_charging():
            if self.agent.stations is None or len(self.agent.stations) < 1:
                logger.warning("Transport {} looking for a station.".format(self.agent.name))
                await self.send_get_stations()
            else:
                station = random.choice(list(self.agent.stations.keys()))
                logger.info("Transport {} reserving station {}.".format(self.agent.name, station))
                await self.send_proposal(station)
                self.agent.status = TRANSPORT_WAITING_FOR_STATION_APPROVAL

        msg = await self.receive(timeout=5)
        if not msg:
            return
        logger.debug("Transport received message: {}".format(msg))
        try:
            content = json.loads(msg.body)
        except TypeError:
            content = {}

        performative = msg.get_metadata("performative")
        protocol = msg.get_metadata("protocol")

        if protocol == QUERY_PROTOCOL:
            if performative == INFORM_PERFORMATIVE:
                self.agent.stations = content
                logger.info("Got list of current stations: {}".format(list(self.agent.stations.keys())))
            elif performative == CANCEL_PERFORMATIVE:
                logger.info("Cancellation of request for stations information.")

        elif protocol == REQUEST_PROTOCOL:
            logger.debug("Transport {} received request protocol from customer/station.".format(self.agent.name))

            if performative == REQUEST_PERFORMATIVE:
                if self.agent.status == TRANSPORT_WAITING:
                    if not self.has_enough_autonomy(content["origin"], content["dest"]):
                        await self.cancel_proposal(content["customer_id"])
                        self.agent.status = TRANSPORT_NEEDS_CHARGING
                    else:
                        await self.send_proposal(content["customer_id"], {})
                        self.agent.status = TRANSPORT_WAITING_FOR_APPROVAL

            elif performative == ACCEPT_PERFORMATIVE:
                if self.agent.status == TRANSPORT_WAITING_FOR_APPROVAL:
                    logger.debug("Transport {} got accept from {}".format(self.agent.name,
                                                                          content["customer_id"]))
                    try:
                        self.agent.status = TRANSPORT_MOVING_TO_CUSTOMER
                        await self.pick_up_customer(content["customer_id"], content["origin"], content["dest"])
                    except PathRequestException:
                        logger.error("Transport {} could not get a path to customer {}. Cancelling..."
                                     .format(self.agent.name, content["customer_id"]))
                        self.agent.status = TRANSPORT_WAITING
                        await self.cancel_proposal(content["customer_id"])
                    except Exception as e:
                        logger.error("Unexpected error in transport {}: {}".format(self.agent.name, e))
                        await self.cancel_proposal(content["customer_id"])
                        self.agent.status = TRANSPORT_WAITING
                else:
                    await self.cancel_proposal(content["customer_id"])

            elif performative == REFUSE_PERFORMATIVE:
                logger.debug("Transport {} got refusal from customer/station".format(self.agent.name))
                self.agent.status = TRANSPORT_WAITING

            elif performative == INFORM_PERFORMATIVE:
                if self.agent.status == TRANSPORT_WAITING_FOR_STATION_APPROVAL:
                    logger.info("Transport {} got accept from station {}".format(self.agent.name,
                                                                                 content["station_id"]))
                    try:
                        self.agent.status = TRANSPORT_MOVING_TO_STATION
                        await self.send_confirmation_travel(content["station_id"])
                        await self.go_to_the_station(content["station_id"], content["dest"])
                    except PathRequestException:
                        logger.error("Transport {} could not get a path to station {}. Cancelling..."
                                     .format(self.agent.name, content["station_id"]))
                        self.agent.status = TRANSPORT_WAITING
                        await self.cancel_proposal(content["station_id"])
                    except Exception as e:
                        logger.error("Unexpected error in transport {}: {}".format(self.agent.name, e))
                        await self.cancel_proposal(content["station_id"])
                        self.agent.status = TRANSPORT_WAITING
                elif self.agent.status == TRANSPORT_CHARGING:
                    if content["status"] == TRANSPORT_CHARGED:
                        self.agent.transport_charged()
                        await self.agent.drop_station()

            elif performative == CANCEL_PERFORMATIVE:
                logger.info("Cancellation of request for {} information".format(self.agent.fleet_type))
Helpers

There are some helper coroutines that are specific for the transport strategy:

async def send_proposal(self, customer_id, content=None)
async def cancel_proposal(self, customer_id, content=None)
async def pick_up_customer(self, customer_id, origin, dest)

The definition and purpose of each of them is now introduced:

  • send_proposal

    This helper function simplifies the composition and sending of a message containing a proposal to a customer. It sends a Message to customer_id using the REQUEST_PROTOCOL and a PROPOSE_PERFORMATIVE. It optionally accepts a content parameter where you can include any additional information you may want the customer to analyze.

  • cancel_proposal

    This helper function simplifies the composition and sending of a message to a customer to cancel a proposal. It sends a Message to customer_id using the REQUEST_PROTOCOL and a CANCEL_PERFORMATIVE. It optionally accepts a content parameter where you can include any additional information you may want the customer to analyze.

  • pick_up_customer

    This helper function triggers the TRAVEL_PROTOCOL of a transport, which is the protocol that is used to transport a customer from her current position to her destination. This is a very important and particular function. Invoking this function is normally the last instruction of this strategy, since it means that the purpose of the strategy is accomplished (until the TRAVEL_PROTOCOL ends and the transport is again free and able to receive new requests from some other customers).

    The pick_up_customer helper receives as parameters the id of the customer and the coordinates of the customer’s current position (origin) and its destination (dest).

Developing the Customer Agent Strategy

To develop a new strategy for the Customer Agent, you need to create a class that inherits from CustomerStrategyBehaviour. Since this is a cyclic behaviour class that follows the Strategy Pattern and that inherits from the StrategyBehaviour, it has all the previously presented helper functions for communication and storing data inside the agent.

The customer strategy is intended to ask a fleet manager agent for a transport service, then wait for transport proposals and, after evaluating them, choosing a particular transport proposal which will take the customer to her destination.

The place in the code where your customer strategy must be coded is the run coroutine. This function is executed in an infinite loop until the agent stops. In addition, you may also overload the on_start and the on_end coroutines, in order to execute code before the creation of the strategy or after its destruction, if needed.

Code

The default strategy of a Customer agent is a dummy strategy that simply accepts the first proposal it receives. This is the code of the default customer strategy AcceptFirstRequestBehaviour:

from simfleet.customer import CustomerStrategyBehaviour

class AcceptFirstRequestTransportBehaviour(CustomerStrategyBehaviour):

    async def run(self):
        if self.agent.fleetmanagers is None:
            await self.send_get_managers(self.agent.fleet_type)

            msg = await self.receive(timeout=5)
            if msg:
                performative = msg.get_metadata("performative")
                if performative == INFORM_PERFORMATIVE:
                    self.agent.fleetmanagers = json.loads(msg.body)
                    return
                elif performative == CANCEL_PERFORMATIVE:
                    logger.info("Cancellation of request for {} information".format(self.agent.type_service))
                    return

        if self.agent.status == CUSTOMER_WAITING:
            await self.send_request(content={})

        msg = await self.receive(timeout=5)

        if msg:
            performative = msg.get_metadata("performative")
            transport_id = msg.sender
            if performative == PROPOSE_PERFORMATIVE:
                if self.agent.status == CUSTOMER_WAITING:
                    logger.debug(
                        "Customer {} received proposal from transport {}".format(self.agent.name, transport_id))
                    await self.accept_transport(transport_id)
                    self.agent.status = CUSTOMER_ASSIGNED
                else:
                    await self.refuse_transport(transport_id)

            elif performative == CANCEL_PERFORMATIVE:
                if self.agent.transport_assigned == str(transport_id):
                    logger.warning(
                        "Customer {} received a CANCEL from Transport {}.".format(self.agent.name, transport_id))
                    self.agent.status = CUSTOMER_WAITING
Helpers

There are some helper coroutines that are specific for the customer strategy:

async def send_get_managers(content=None)
async def send_request(self, content=None)
async def accept_transport(self, transport_aid)
async def refuse_transport(self, transport_aid)

The definition and purpose of each of them is now introduced:

  • send_get_managers

    This helper makes a query to the Directory agent to find all the fleet managers that provide a fleet service of type content. Thus, you can filter those fleet managers that provide the transport service that you are looking for. It is expected for the user to store the response of this query in the self.agent.fleetmanagers variable as a dictionary. This variable will be used by the next helper.

  • send_request

    This helper is useful to make a new request without building the entire message (the function makes it for you). It creates a Message with a REQUEST performative and sends it to all the fleet manager agents stored in self.agent.fleetmanagers. In addition, you can append a content to the request message to be used by the fleet manager agent or the transport agents (e.g. your origin coordinates or your destination coordinates).

  • accept_transport

    This is a helper function to send an acceptance message to a transport_id. It sends a Message with an ACCEPT performative to the selected transport.

  • refuse_transport

    This is a helper function to refuse a proposal from a transport_id. It sends a Message with an REFUSE performative to the transport whose proposal is being refused.

Other Helpers

SimFleet also includes a helpers module which provides some general support methods that may be useful for any agent. These functions are now introduced:

  • are_close

    This helper function facilitates working with distances in maps. This helper function accepts two coordinates (coord1 and coord2) and an optional parameter to set the tolerance in meters. It returns True if both coordinates are closer than the tolerance in meters (10 meters by default). Otherwise it returns False.

    Example:

    assert are_close([39.253, -0.341], [39.351, -0.333], 1000) == True
    
  • distance_in_meters

    This helper function returns the distance in meters between two points.

    Example:

    assert distance_in_meters([-0.37565, 39.44447], [-0.40392, 39.45293]) == 3264.7134341427977
    

How to Implement New Strategies – Recommendations

At this point is time for you to implement your own strategies to optimize the problem of dispatching transports to customers. In this chapter we have shown you the tools to create these strategies. You have to create a file (in this example we are using my_strategy_file.py) and develop the strategies to be tested following the next template:

from simfleet.fleetmanager import FleetManagerStrategyBehaviour
from simfleet.customer import CustomerStrategyBehaviour
from simfleet.transport import TransportStrategyBehaviour

################################################################
#                                                              #
#                     FleetManager Strategy                    #
#                                                              #
################################################################
class MyFleetManagerStrategy(FleetManagerStrategyBehaviour):
    async def run(self):
       # Your code here

################################################################
#                                                              #
#                         Transport Strategy                   #
#                                                              #
################################################################
class MyTransportStrategy(TransportStrategyBehaviour):
    async def run(self):
       # Your code here

################################################################
#                                                              #
#                       Customer Strategy                      #
#                                                              #
################################################################
class MyCustomerStrategy(CustomerStrategyBehaviour):
    async def run(self):
       # Your code here

In this file, three strategies have been created for the three types of agent handled by the simulator. We have called these strategies MyFleetManagerStrategy, MyTransportStrategy and MyCustomerStrategy.

To run the simulator with your new strategies the configuration file accepts three parameters with the name of the file (without extension) and the name of the class of each strategy.

{
    "fleets": [...],
    "transports": [...],
    "customers": [...],
    "stations": [...],
    "simulation_name": "My Config",
    "max_time": 1000,
    "transport_strategy": "my_strategy_file.MyTransportStrategy",
    "customer_strategy": "my_strategy_file.MyCustomerStrategy",
    "fleetmanager_strategy": "my_strategy_file.MyFleetManagerStrategy",
    ...
    "host": "localhost",
}
$ simfleet --config my_custom_simulation.json

Warning

The file must be in the current working directory and it must be referenced without the extension (if the file is named my_strategy_file.py use my_strategy_file when calling the simulator.

Once run the simulator you can test your strategies using the graphical web interface or by inspecting the output of the logs in the command line.

[GangOfFour95]
  1. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns, Elements of Reusable Object Oriented Software. Addison-Wesley, 1995.

API Documentation

Information on specific functions, classes, and methods.

simfleet package

Submodules

simfleet.cli module

Console script for SimFleet.

simfleet.config module

class simfleet.config.SimfleetConfig(filename=None, name=None, max_time=None, verbose=None)[source]

Bases: object

A scenario object reads a file with a JSON representation of a scenario and is used to create the participant agents.

load_config(filename)[source]
num_customers
num_managers
num_stations
num_transport
simfleet.config.hide_passwords(item, key=None)[source]

simfleet.customer module

class simfleet.customer.CustomerAgent(agentjid, password)[source]

Bases: spade.agent.Agent

get_pickup_time()[source]

Returns the time that the customer was waiting to be picked up since it has been assigned to a transport.

Returns:The time that the customer was waiting to a transport since it has been assigned.
Return type:float
get_position()[source]

Returns the current position of the customer.

Returns:the coordinates of the current position of the customer (lon, lat)
Return type:list
get_waiting_time()[source]

Returns the time that the agent was waiting for a transport, from its creation until it gets into a transport.

Returns:The time the customer was waiting.
Return type:float
is_in_destination()[source]

Checks if the customer has arrived to its destination.

Returns:whether the customer is at its destination or not
Return type:bool
request_path(origin, destination)[source]

Requests a path between two points (origin and destination) using the RouteAgent service.

Parameters:
  • origin (list) – the coordinates of the origin of the requested path
  • destination (list) – the coordinates of the end of the requested path
Returns:

A list of points that represent the path from origin to destination, the distance and the estimated duration

Return type:

list, float, float

run_strategy()[source]

import json Runs the strategy for the customer agent.

set_directory(directory_id)[source]

Sets the directory JID address :param directory_id: the DirectoryAgent jid :type directory_id: str

set_fleet_type(fleet_type)[source]

Sets the type of fleet to be used.

Parameters:fleet_type (str) – the type of the fleet to be used
set_fleetmanager(fleetmanagers)[source]

Sets the fleetmanager JID address :param fleetmanagers: the fleetmanager jid :type fleetmanagers: str

set_icon(icon)[source]
set_id(agent_id)[source]

Sets the agent identifier :param agent_id: The new Agent Id :type agent_id: str

set_position(coords=None)[source]

Sets the position of the customer. If no position is provided it is located in a random position.

Parameters:coords (list) – a list coordinates (longitude and latitude)
set_route_agent(route_id)[source]

Sets the route agent JID address :param route_id: the route agent jid :type route_id: str

set_target_position(coords=None)[source]

Sets the target position of the customer (i.e. its destination). If no position is provided the destination is setted to a random position.

Parameters:coords (list) – a list coordinates (longitude and latitude)
setup()[source]

Setup agent before startup. This coroutine may be overloaded.

to_json()[source]

Serializes the main information of a customer agent to a JSON format. It includes the id of the agent, its current position, the destination coordinates of the agent, the current status, the transport that it has assigned (if any) and its waiting time.

Returns:a JSON doc with the main information of the customer.

Example:

{
    "id": "cphillips",
    "position": [ 39.461327, -0.361839 ],
    "dest": [ 39.460599, -0.335041 ],
    "status": 24,
    "transport": "ghiggins@127.0.0.1",
    "waiting": 13.45
}
Return type:dict
total_time()[source]

Returns the time since the customer was activated until it reached its destination.

Returns:the total time of the customer’s simulation.
Return type:float
class simfleet.customer.CustomerStrategyBehaviour[source]

Bases: simfleet.utils.StrategyBehaviour

Class from which to inherit to create a transport strategy. You must overload the run coroutine

Helper functions:
  • send_request
  • accept_transport
  • refuse_transport
accept_transport(transport_id)[source]

Sends a spade.message.Message to a transport to accept a travel proposal. It uses the REQUEST_PROTOCOL and the ACCEPT_PERFORMATIVE.

Parameters:transport_id (str) – The Agent JID of the transport
on_start()[source]

Initializes the logger and timers. Call to parent method if overloaded.

refuse_transport(transport_id)[source]

Sends an spade.message.Message to a transport to refuse a travel proposal. It uses the REQUEST_PROTOCOL and the REFUSE_PERFORMATIVE.

Parameters:transport_id (str) – The Agent JID of the transport
run()[source]

Body of the behaviour. To be implemented by user.

send_get_managers(content=None)[source]

Sends an spade.message.Message to the DirectoryAgent to request a managers. It uses the QUERY_PROTOCOL and the REQUEST_PERFORMATIVE. If no content is set a default content with the type_service that needs :param content: Optional content dictionary :type content: dict

send_request(content=None)[source]

Sends an spade.message.Message to the fleetmanager to request a transport. It uses the REQUEST_PROTOCOL and the REQUEST_PERFORMATIVE. If no content is set a default content with the customer_id, origin and target coordinates is used.

Parameters:content (dict) – Optional content dictionary
class simfleet.customer.TravelBehaviour[source]

Bases: spade.behaviour.CyclicBehaviour

This is the internal behaviour that manages the movement of the customer. It is triggered when the transport informs the customer that it is going to the customer’s position until the customer is droppped in its destination.

on_start()[source]

Coroutine called before the behaviour is started.

run()[source]

Body of the behaviour. To be implemented by user.

simfleet.directory module

class simfleet.directory.DirectoryAgent(agentjid, password)[source]

Bases: spade.agent.Agent

run_strategy()[source]

Runs the strategy for the directory agent.

set_id(agent_id)[source]

Sets the agent identifier

Parameters:agent_id (str) – The new agent id
setup()[source]

Setup agent before startup. This coroutine may be overloaded.

class simfleet.directory.DirectoryStrategyBehaviour[source]

Bases: simfleet.utils.StrategyBehaviour

Class from which to inherit to create a directory strategy.

on_start()[source]

Coroutine called before the behaviour is started.

run()[source]

Body of the behaviour. To be implemented by user.

send_negative(agent_id)[source]

Sends a message to the current assigned manager/station to cancel the registration.

Parameters:agent_id (str) – the id of the manager/station
send_services(agent_id, type_service)[source]

Send a message to the customer or transport with the current information of the type of service they need.

Parameters:
  • agent_id (str) – the id of the manager/station
  • type_service (str) – the type of service
class simfleet.directory.RegistrationBehaviour[source]

Bases: spade.behaviour.CyclicBehaviour

add_service(content)[source]

Adds a new service to the store.

Parameters:content (dict) – content to be added
on_start()[source]

Coroutine called before the behaviour is started.

remove_service(service_type, agent)[source]

Erase a service from the store.

Parameters:
  • service_type (str) – the service type to be erased
  • agent (str) – an str with the jid of the agent to be erased
run()[source]

Body of the behaviour. To be implemented by user.

send_confirmation(agent_id)[source]

Send a spade.message.Message with an acceptance to manager/station to register in the dictionary

simfleet.fleetmanager module

class simfleet.fleetmanager.FleetManagerAgent(agentjid, password)[source]

Bases: spade.agent.Agent

FleetManager agent that manages the requests between transports and customers

clear_agents()[source]

Resets the set of transports and customers. Resets the simulation clock.

run_strategy()[source]

Runs the strategy for the transport agent.

set_directory(directory_id)[source]

Sets the directory JID address :param directory_id: the DirectoryAgent jid :type directory_id: str

set_fleet_type(fleet_type)[source]

Sets the type of service to the fleet :param type_service: type of service :type type_service: str

set_icon(icon)[source]
set_id(agent_id)[source]

Sets the agent identifier

Parameters:agent_id (str) – The new Agent Id
set_registration(status)[source]

Sets the status of registration :param status: True if the transport agent has registered or False if not :type status: boolean

setup()[source]

Setup agent before startup. This coroutine may be overloaded.

class simfleet.fleetmanager.FleetManagerStrategyBehaviour[source]

Bases: simfleet.utils.StrategyBehaviour

Class from which to inherit to create a coordinator strategy. You must overload the _process() method

Helper functions:
get_transport_agents()[source]

Gets the list of registered transports

Returns:a list of TransportAgent
Return type:list
on_start()[source]

Coroutine called before the behaviour is started.

run()[source]

Body of the behaviour. To be implemented by user.

send_registration()[source]

Send a spade.message.Message with a proposal to directory to register.

class simfleet.fleetmanager.TransportRegistrationForFleetBehaviour[source]

Bases: spade.behaviour.CyclicBehaviour

accept_registration(agent_id)[source]

Send a spade.message.Message with an acceptance to transport to register in the fleet.

add_transport(agent)[source]

Adds a new TransportAgent to the store.

Parameters:agent (TransportAgent) – the instance of the TransportAgent to be added
on_start()[source]

Coroutine called before the behaviour is started.

reject_registration(agent_id)[source]

Send a spade.message.Message with an acceptance to transport to register in the fleet.

remove_transport(key)[source]

Erase a TransportAgent to the store.

Parameters:agent (TransportAgent) – the instance of the TransportAgent to be erased
run()[source]

Body of the behaviour. To be implemented by user.

simfleet.helpers module

Helpers module

These functions are useful for the develop of new strategies.

exception simfleet.helpers.AlreadyInDestination[source]

Bases: Exception

This exception is raised when an agent wants to move to a destination where it is already there.

exception simfleet.helpers.PathRequestException[source]

Bases: Exception

This exception is raised when a path could not be computed.

simfleet.helpers.are_close(coord1, coord2, tolerance=10)[source]

Checks wheter two points are close or not. The tolerance is expressed in meters.

Parameters:
  • coord1 (list) – a coordinate (longitude, latitude)
  • coord2 (list) – another coordinate (longitude, latitude)
  • tolerance (int) – tolerance in meters
Returns:

whether the two coordinates are closer than tolerance or not

Return type:

bool

simfleet.helpers.distance_in_meters(coord1, coord2)[source]

Returns the distance between two coordinates in meters.

Parameters:
  • coord1 (list) – a coordinate (longitude, latitude)
  • coord2 – another coordinate (longitude, latitude)
Returns:

distance meters between the two coordinates

Return type:

float

simfleet.helpers.kmh_to_ms(speed_in_kmh)[source]

Convert kilometers/hour to meters/second.

Parameters:speed_in_kmh (float) – speed in kilometers/hour
Returns:the speed in meters/second
Return type:float
simfleet.helpers.random_position()[source]

Returns a random position inside the map.

Returns:a point (longitude and latitude)
Return type:list

simfleet.protocol module

protocol and performative constants

simfleet.route module

class simfleet.route.RouteAgent(agentjid, password)[source]

Bases: spade.agent.Agent

The RouteAgent receives request for paths, queries an OSRM server and returns the information. It also caches the queries to avoid overloading the OSRM server.

class RequestRouteBehaviour[source]

Bases: spade.behaviour.CyclicBehaviour

This cyclic behaviour listens for route requests from other agents. When a message is received it answers with the path.

on_end()[source]

Coroutine called after the behaviour is done or killed.

on_start()[source]

Coroutine called before the behaviour is started.

run()[source]

Body of the behaviour. To be implemented by user.

get_route(origin, destination)[source]

Checks the cache for a path, if not found then it queries the OSRM server.

Parameters:
  • origin (list) – origin coordinate (longitude, latitude)
  • destination (list) – target coordinate (longitude, latitude)
Returns:

a dict with three keys: path, distance and duration

Return type:

dict

load_cache()[source]

Loads the cache from file.

persist_cache()[source]

Persists the cache to a JSON file.

static request_route_to_server(origin, destination)[source]

Queries the OSRM for a path.

Parameters:
  • origin (list) – origin coordinate (longitude, latitude)
  • destination (list) – target coordinate (longitude, latitude)
Returns:

list, float, float = the path, the distance of the path and the estimated duration

setup()[source]

Setup agent before startup. This coroutine may be overloaded.

simfleet.simulator module

class simfleet.simulator.SimulatorAgent(config, agentjid='simulator@localhost', password='simulator123j3')[source]

Bases: spade.agent.Agent

The Simulator. It manages all the simulation processes. Tasks done by the simulator at initialization:

  1. Create the XMPP server
  2. Run the SPADE backend
  3. Run the directory and route agents.
  4. Create agents defined in scenario (if any).

After these tasks are done in the Simulator constructor, the simulation is started when the run method is called.

add_customer(agent)[source]

Adds a new CustomerAgent to the store.

Parameters:agent (CustomerAgent) – the instance of the CustomerAgent to be added
add_manager(agent)[source]

Adds a new FleetManagerAgent to the store.

Parameters:agent (FleetManagerAgent) – the instance of the FleetManagerAgent to be added
add_station(agent)[source]

Adds a new StationAgent to the store.

Parameters:agent (StationAgent) – the instance of the StationAgent to be added
add_transport(agent)[source]

Adds a new TransportAgent to the store.

Parameters:agent (TransportAgent) – the instance of the TransportAgent to be added
all_customers_in_destination()[source]

Checks whether the simulation has finished or not. A simulation is finished if all customers are at their destinations. If there is no customers the simulation is not finished.

Returns:`
bool: whether the simulation has finished or not.
assigning_fleet_icon(fleet_type, default=None)[source]
async_start_agent(agent)[source]
clean_controller(request)[source]

Web controller that resets the simulator to a clean state.

Returns:no template is returned since this is an AJAX controller, a dict with status=done
Return type:dict
clear_agents()[source]

Resets the set of transports and customers. Resets the simulation clock.

clear_stopped_agents()[source]

Removes from the transport and customer sets every agent that is stopped.

collect_stats()[source]

Collects stats from all participant agents and from the simulation and stores it in three dataframes.

create_customer_agent(name, password, fleet_type, position, strategy=None, target=None)[source]

Create a customer agent.

Parameters:
  • name (str) – name of the agent
  • password (str) – password of the agent
  • position (list) – initial coordinates of the agent
  • fleet_type (str) – type of he fleet to be or demand
  • target (list, optional) – destination coordinates of the agent
  • speed (float, optional) – speed of the vehicle
create_directory_agent(name, password)[source]
create_fleetmanager_agent(name, password, fleet_type, strategy=None, icon=None)[source]
create_station_agent(name, password, position, power, places, strategy=None)[source]

Create a customer agent.

Parameters:
  • name (str) – name of the agent
  • password (str) – password of the agent
  • position (list) – initial coordinates of the agent
  • fleet_type (str) – type of he fleet to be or demand
  • target (list, optional) – destination coordinates of the agent
  • speed (float, optional) – speed of the vehicle
create_transport_agent(name, password, fleet_type, fleetmanager, position, strategy=None, speed=None, autonomy=None, current_autonomy=None)[source]
customer_agents

Gets the dict of registered customers

Returns:a dict of CustomerAgent with the name in the key
Return type:dict
download_stats_excel_controller(request)[source]

Web controller that returns an Excel file with the simulation results.

Returns:a Response of type “attachment” with the file content.
Return type:Response
download_stats_json_controller(request)[source]

Web controller that returns a JSON file with the simulation results.

Returns:a Response of type “attachment” with the file content.
Return type:Response
entities_controller(request)[source]

Web controller that returns a dict with the entities of the simulator and their statuses.

Example of the entities returned data:

{
    "customers": [
        {
            "status": 24,
            "transport": "transport2@127.0.0.1",
            "dest": [ 39.463356, -0.376463 ],
            "waiting": 3.25,
            "position": [ 39.460568, -0.352529 ],
            "id": "michaelstewart"
        }
    ],
    "transports": [
        {
            "status": 11,
            "customer": "michaelstewart@127.0.0.1",
            "assignments": 1,
            "path": [
                     [ 39.478328, -0.406712 ],
                     [ 39.478317, -0.406814 ],
                     [ 39.460568, -0.352529 ]
                    ],
            "dest": [ 39.460568, -0.352529 ],
            "position": [ 39.468131, -0.39685 ],
            "speed": 327.58,
            "id": "transport2",
            "distance": "6754.60"
        }
    ],
    "stats": {
        "totaltime": "-1.00",
        "waiting": "3.25",
        "finished": False,
        "is_running": True
    },
    "tree": {
        "name": "Agents",
        "children": [
            {
                "count": "1",
                "name": "Transports",
                "children": [ { "status": 11, "name": " transport2", "icon": "fa-transport" } ]
            },
            {
                "count": "1",
                "name": "Customers",
                "children": [ { "status": 24, "name": " michaelstewart", "icon": "fa-user" } ]
            }
        ]
    },
    "authenticated": False,
    "stations": [
        {
            "status": 24,
            "position": [ 39.460568, -0.352529 ],
            "id": "michaelstewart"
        }
    ],
}
Returns:no template is returned since this is an AJAX controller, a dict with the list of transports, the list of customers, the tree view to be showed in the sidebar and the stats of the simulation.
Return type:dict
generate_tree()[source]

Generates the tree view in JSON format to be showed in the sidebar.

Returns:a dict with all the agents in the simulator, with their name, status and icon.
Return type:dict
get_customer_stats()[source]

Creates a dataframe with the simulation stats of the customers The dataframe includes for each customer its name, waiting time, total time and status.

Returns:the dataframe with the customers stats.
Return type:pandas.DataFrame
get_directory()[source]
get_manager_stats()[source]

Creates a dataframe with the simulation stats of the customers The dataframe includes for each customer its name, waiting time, total time and status.

Returns:the dataframe with the customers stats.
Return type:pandas.DataFrame
get_simulation_time()[source]

Returns the elapsed simulation time to the current time. If the simulation is not started it returns 0.

Returns:the whole simulation time.
Return type:float
get_station_stats()[source]

Creates a dataframe with the simulation stats of the customers The dataframe includes for each customer its name, waiting time, total time and status.

Returns:the dataframe with the customers stats.
Return type:pandas.DataFrame
get_stats()[source]

Generates the stats of the simulation in JSON format.

Examples:

{
    "totaltime": "12.25",
    "waiting": "3.25",
    "finished": False,
    "is_running": True
}
Returns:a dict with the total time, waiting time, is_running and finished values
Return type:dict
get_stats_dataframes()[source]

Collects simulation stats and returns 3 dataframes with the information: A general dataframe with the average information, a dataframe with the transport’s information and a dataframe with the customer’s information. :returns: avg df, transport df and customer df :rtype: pandas.Dataframe, pandas.Dataframe, pandas.Dataframe

get_transport_stats()[source]

Creates a dataframe with the simulation stats of the transports The dataframe includes for each transport its name, assignments, traveled distance and status.

Returns:the dataframe with the transports stats.
Return type:pandas.DataFrame
index_controller(request)[source]

Web controller that returns the index page of the simulator.

Returns:the name of the template, the data to be pre-processed in the template
Return type:dict
init_controller(request)[source]
is_simulation_finished()[source]

Checks if the simulation is finished. A simulation is finished if the max simulation time has been reached or when the fleetmanager says it.

Returns:whether the simulation is finished or not.
Return type:bool
load_icons(filename)[source]
load_scenario()[source]

Load the information from the preloaded scenario through the SimfleetConfig class

manager_agents

Gets the dict of registered FleetManager

Returns:a dict of FleetManagerAgents with the name in the key
Return type:dict
print_stats()[source]

Prints the dataframes collected by collect_stats.

request_path(origin, destination)[source]

Requests a path to the RouteAgent.

Parameters:
  • origin (list) – the origin coordinates (lon, lat)
  • destination (list) – the target coordinates (lon, lat)
Returns:

the path as a list of points, the distance of the path, the estimated duration of the path

Return type:

list, float, float

run()[source]

Starts the simulation

run_controller(request)[source]

Web controller that starts the simulator.

Returns:no template is returned since this is an AJAX controller, an empty data dict is returned
Return type:dict
set_default_strategies(fleetmanager_strategy, transport_strategy, customer_strategy, directory_strategy, station_strategy)[source]

Gets the strategy strings and loads their classes. This strategies are prepared to be injected into any new transport or customer agent.

Parameters:
  • fleetmanager_strategy (str) – the path to the fleetmanager strategy
  • transport_strategy (str) – the path to the transport strategy
  • customer_strategy (str) – the path to the customer strategy
  • directory_strategy (str) – the path to the directory strategy
  • station_strategy (str) – the path to the station strategy
set_directory(agent)[source]
set_icon(agent, icon, default=None)[source]
setup()[source]

Setup agent before startup. This coroutine may be overloaded.

station_agents

Gets the dict of registered stations

Returns:a dict of StationAgent with the name in the key
Return type:dict
stop()[source]

Finishes the simulation and prints simulation stats. Tasks done when a simulation is stopped:

  1. Stop participant agents.
  2. Print stats.
  3. Stop Route agent.
  4. Stop fleetmanager agent.
stop_agents()[source]

Stops the simulator and all the agents

stop_agents_controller(request)[source]

Web controller that stops all the customer and transport agents.

Returns:no template is returned since this is an AJAX controller, a dict with status=done
Return type:dict
time_is_out()[source]

Checks if the max simulation time has been reached.

Returns:whether the max simulation time has been reached or not.
Return type:bool
transport_agents

Gets the dict of registered transports

Returns:a dict of TransportAgent with the name in the key
Return type:dict
write_excel(filename)[source]

Writes the collected data by collect_stats in an excel file.

Parameters:filename (str) – name of the excel file.
write_file(filename, fileformat='json')[source]

Writes the dataframes collected by collect_stats in JSON or Excel format.

Parameters:
  • filename (str) – name of the output file to be written.
  • fileformat (str) – format of the output file. Choices: json or excel
write_json(filename)[source]

Writes the collected data by collect_stats in a json file.

Parameters:filename (str) – name of the json file.

simfleet.station module

class simfleet.station.ChargeBehaviour(start_at, transport_id)[source]

Bases: spade.behaviour.TimeoutBehaviour

charging_complete()[source]

Send a message to the transport agent that the vehicle load has been completed

run()[source]

Body of the behaviour. To be implemented by user.

class simfleet.station.RegistrationBehaviour[source]

Bases: spade.behaviour.CyclicBehaviour

on_start()[source]

Coroutine called before the behaviour is started.

run()[source]

Body of the behaviour. To be implemented by user.

send_registration()[source]

Send a spade.message.Message with a proposal to directory to register.

set_registration(decision)[source]
class simfleet.station.StationAgent(agentjid, password)[source]

Bases: spade.agent.Agent

assigning_place()[source]

Set a space in the charging station for the transport that has been accepted, when the available spaces are zero, the status will change to BUSY_STATION

charging_transport(need, transport_id)[source]
deassigning_place()[source]

Leave a space of the charging station, when the station has free spaces, the status will change to FREE_STATION

get_available_places()[source]
get_position()[source]

Returns the current position of the station.

Returns:the coordinates of the current position of the customer (lon, lat)
Return type:list
get_power()[source]
get_status()[source]
run_strategy()[source]

Sets the strategy for the transport agent.

set_available_places(places)[source]
set_directory(directory_id)[source]

Sets the directory JID address :param directory_id: the DirectoryAgent jid :type directory_id: str

set_icon(icon)[source]
set_id(agent_id)[source]

Sets the agent identifier

Parameters:agent_id (str) – The new Agent Id
set_position(coords=None)[source]

Sets the position of the station. If no position is provided it is located in a random position.

Parameters:coords (list) – a list coordinates (longitude and latitude)
set_power(charge)[source]
set_registration(status)[source]

Sets the status of registration :param status: True if the transport agent has registered or False if not :type status: boolean

set_status(state='FREE_STATION')[source]
set_type(station_type)[source]
setup()[source]

Setup agent before startup. This coroutine may be overloaded.

to_json()[source]

Serializes the main information of a station agent to a JSON format. It includes the id of the agent, its current position, the destination coordinates of the agent, the current status, the transport that it has assigned (if any) and its waiting time.

Returns:a JSON doc with the main information of the station.

Example:

{
    "id": "cphillips",
    "position": [ 39.461327, -0.361839 ],
    "status": True,
    "places": 10,
    "power": 10
}
Return type:dict
class simfleet.station.StationStrategyBehaviour[source]

Bases: simfleet.utils.StrategyBehaviour

Class from which to inherit to create a station strategy. You must overload the run() method

Helper functions:
  • get_transport_agents()
accept_transport(transport_id)[source]

Sends a spade.message.Message to a transport to accept a travel proposal for charge. It uses the REQUEST_PROTOCOL and the ACCEPT_PERFORMATIVE.

Parameters:transport_id (str) – The Agent JID of the transport
on_start()[source]

Coroutine called before the behaviour is started.

refuse_transport(transport_id)[source]

Sends an spade.message.Message to a transport to refuse a travel proposal for charge. It uses the REQUEST_PROTOCOL and the REFUSE_PERFORMATIVE.

Parameters:transport_id (str) – The Agent JID of the transport
run()[source]

Body of the behaviour. To be implemented by user.

class simfleet.station.TravelBehaviour[source]

Bases: spade.behaviour.CyclicBehaviour

This is the internal behaviour that manages the inform of the station. It is triggered when the transport informs the station that it is going to the customer’s position until the customer is droppped in its destination.

on_start()[source]

Coroutine called before the behaviour is started.

run()[source]

Body of the behaviour. To be implemented by user.

simfleet.strategies module

class simfleet.strategies.AcceptAlwaysStrategyBehaviour[source]

Bases: simfleet.transport.TransportStrategyBehaviour

The default strategy for the Transport agent. By default it accepts every request it receives if available.

run()[source]

Body of the behaviour. To be implemented by user.

class simfleet.strategies.AcceptFirstRequestBehaviour[source]

Bases: simfleet.customer.CustomerStrategyBehaviour

The default strategy for the Customer agent. By default it accepts the first proposal it receives.

run()[source]

Body of the behaviour. To be implemented by user.

class simfleet.strategies.DelegateRequestBehaviour[source]

Bases: simfleet.fleetmanager.FleetManagerStrategyBehaviour

The default strategy for the FleetManager agent. By default it delegates all requests to all transports.

run()[source]

Body of the behaviour. To be implemented by user.

simfleet.strategies_fsm module

class simfleet.strategies_fsm.FSMTransportStrategyBehaviour[source]

Bases: spade.behaviour.FSMBehaviour

setup()[source]
class simfleet.strategies_fsm.TransportMovingState[source]

Bases: simfleet.transport.TransportStrategyBehaviour, spade.behaviour.State

on_start()[source]

Coroutine called before the behaviour is started.

run()[source]

Body of the behaviour. To be implemented by user.

class simfleet.strategies_fsm.TransportWaitingForApprovalState[source]

Bases: simfleet.transport.TransportStrategyBehaviour, spade.behaviour.State

on_start()[source]

Coroutine called before the behaviour is started.

run()[source]

Body of the behaviour. To be implemented by user.

class simfleet.strategies_fsm.TransportWaitingState[source]

Bases: simfleet.transport.TransportStrategyBehaviour, spade.behaviour.State

on_start()[source]

Coroutine called before the behaviour is started.

run()[source]

Body of the behaviour. To be implemented by user.

simfleet.strategies_fsm.passenger_in_transport_callback(old, new)[source]

simfleet.transport module

class simfleet.transport.RegistrationBehaviour[source]

Bases: spade.behaviour.CyclicBehaviour

on_start()[source]

Coroutine called before the behaviour is started.

run()[source]

Body of the behaviour. To be implemented by user.

send_registration()[source]

Send a spade.message.Message with a proposal to manager to register.

class simfleet.transport.TransportAgent(agentjid, password)[source]

Bases: spade.agent.Agent

class MovingBehaviour(period, start_at=None)[source]

Bases: spade.behaviour.PeriodicBehaviour

This is the internal behaviour that manages the movement of the transport. It is triggered when the transport has a new destination and the periodic tick is recomputed at every step to show a fine animation. This moving behaviour includes to update the transport coordinates as it moves along the path at the specified speed.

run()[source]

Body of the behaviour. To be implemented by user.

arrived_to_destination()[source]

Informs that the transport has arrived to its destination. It recomputes the new destination and path if picking up a customer or drops it and goes to WAITING status again.

arrived_to_station()[source]

Informs that the transport has arrived to its destination. It recomputes the new destination and path if picking up a customer or drops it and goes to WAITING status again.

calculate_km_expense(origin, start, dest=None)[source]
cancel_customer(data=None)[source]

Sends a message to the current assigned customer to cancel the assignment.

Parameters:data (dict, optional) – Complementary info about the cancellation
drop_customer()[source]

Drops the customer that the transport is carring in the current location.

drop_station()[source]

Drops the customer that the transport is carring in the current location.

get_autonomy()[source]
get_position()[source]

Returns the current position of the customer.

Returns:the coordinates of the current position of the customer (lon, lat)
Return type:list
inform_customer(status, data=None)[source]

Sends a message to the current assigned customer to inform her about a new status.

Parameters:
  • status (int) – The new status code
  • data (dict, optional) – complementary info about the status
inform_station(data=None)[source]

Sends a message to the current assigned customer to inform her about a new status.

Parameters:
  • status (int) – The new status code
  • data (dict, optional) – complementary info about the status
is_customer_in_transport()[source]
is_free()[source]
is_in_destination()[source]

Checks if the transport has arrived to its destination.

Returns:whether the transport is at its destination or not
Return type:bool
move_to(dest)[source]

Moves the transport to a new destination.

Parameters:dest (list) – the coordinates of the new destination (in lon, lat format)
Raises:AlreadyInDestination – if the transport is already in the destination coordinates.
needs_charging()[source]
request_path(origin, destination)[source]

Requests a path between two points (origin and destination) using the RouteAgent service.

Parameters:
  • origin (list) – the coordinates of the origin of the requested path
  • destination (list) – the coordinates of the end of the requested path
Returns:

A list of points that represent the path from origin to destination, the distance and the estimated duration

Return type:

list, float, float

Examples

>>> path, distance, duration = await self.request_path(origin=[0,0], destination=[1,1])
>>> print(path)
[[0,0], [0,1], [1,1]]
>>> print(distance)
2.0
>>> print(duration)
3.24
run_strategy()[source]

Sets the strategy for the transport agent.

Parameters:strategy_class (TransportStrategyBehaviour) – The class to be used. Must inherit from TransportStrategyBehaviour
send(msg)[source]
set_autonomy(autonomy, current_autonomy=None)[source]
set_directory(directory_id)[source]

Sets the directory JID address :param directory_id: the DirectoryAgent jid :type directory_id: str

set_fleet_type(fleet_type)[source]
set_fleetmanager(fleetmanager_id)[source]

Sets the fleetmanager JID address :param fleetmanager_id: the fleetmanager jid :type fleetmanager_id: str

set_icon(icon)[source]
set_id(agent_id)[source]

Sets the agent identifier

Parameters:agent_id (str) – The new Agent Id
set_initial_position(coords)[source]
set_km_expense(expense=0)[source]
set_position(coords=None)[source]

Sets the position of the transport. If no position is provided it is located in a random position.

Parameters:coords (list) – a list coordinates (longitude and latitude)
set_registration(status, content=None)[source]

Sets the status of registration :param status: True if the transport agent has registered or False if not :type status: boolean :param content: :type content: dict

set_route_agent(route_id)[source]

Sets the route agent JID address :param route_id: the route agent jid :type route_id: str

set_speed(speed_in_kmh)[source]

Sets the speed of the transport.

Parameters:speed_in_kmh (float) – the speed of the transport in km per hour
setup()[source]

Setup agent before startup. This coroutine may be overloaded.

step()[source]

Advances one step in the simulation

to_json()[source]

Serializes the main information of a transport agent to a JSON format. It includes the id of the agent, its current position, the destination coordinates of the agent, the current status, the speed of the transport (in km/h), the path it is following (if any), the customer that it has assigned (if any), the number of assignments if has done and the distance that the transport has traveled.

Returns:a JSON doc with the main information of the transport.

Example:

{
    "id": "cphillips",
    "position": [ 39.461327, -0.361839 ],
    "dest": [ 39.460599, -0.335041 ],
    "status": 24,
    "speed": 1000,
    "path": [[0,0], [0,1], [1,0], [1,1], ...],
    "customer": "ghiggins@127.0.0.1",
    "assignments": 2,
    "distance": 3481.34
}
Return type:dict
transport_charged()[source]
watch_value(key, callback)[source]

Registers an observer callback to be run when a value is changed

Parameters:
  • key (str) – the name of the value
  • callback (function) – a function to be called when the value changes. It receives two arguments: the old and the new value.
class simfleet.transport.TransportStrategyBehaviour[source]

Bases: simfleet.utils.StrategyBehaviour

Class from which to inherit to create a transport strategy. You must overload the `run coroutine

Helper functions:
  • pick_up_customer
  • send_proposal
  • cancel_proposal
cancel_proposal(customer_id, content=None)[source]

Send a spade.message.Message to cancel a proposal. If the content is empty the proposal is sent without content.

Parameters:
  • customer_id (str) – the id of the customer
  • content (dict, optional) – the optional content of the message
go_to_the_station(station_id, dest)[source]

Starts a TRAVEL_PROTOCOL to pick up a customer and get him to his destination. It automatically launches all the travelling process until the customer is delivered. This travelling process includes to update the transport coordinates as it moves along the path at the specified speed.

Parameters:
  • customer_id (str) – the id of the customer
  • origin (list) – the coordinates of the current location of the customer
  • dest (list) – the coordinates of the target destination of the customer
has_enough_autonomy(customer_orig, customer_dest)[source]
on_start()[source]

Coroutine called before the behaviour is started.

pick_up_customer(customer_id, origin, dest)[source]

Starts a TRAVEL_PROTOCOL to pick up a customer and get him to his destination. It automatically launches all the travelling process until the customer is delivered. This travelling process includes to update the transport coordinates as it moves along the path at the specified speed.

Parameters:
  • customer_id (str) – the id of the customer
  • origin (list) – the coordinates of the current location of the customer
  • dest (list) – the coordinates of the target destination of the customer
run()[source]

Body of the behaviour. To be implemented by user.

send_confirmation_travel(station_id)[source]
send_get_stations(content=None)[source]
send_proposal(customer_id, content=None)[source]

Send a spade.message.Message with a proposal to a customer to pick up him. If the content is empty the proposal is sent without content.

Parameters:
  • customer_id (str) – the id of the customer
  • content (dict, optional) – the optional content of the message

simfleet.utils module

class simfleet.utils.RequestRouteBehaviour(msg: spade.message.Message, origin: list, destination: list, route_agent: str)[source]

Bases: spade.behaviour.OneShotBehaviour

A one-shot behaviour that is executed to request for a new route to the route agent.

run()[source]

Body of the behaviour. To be implemented by user.

class simfleet.utils.StrategyBehaviour[source]

Bases: spade.behaviour.CyclicBehaviour

The behaviour that all parent strategies must inherit from. It complies with the Strategy Pattern.

simfleet.utils.avg(array)[source]

Makes the average of an array without Nones. :param array: a list of floats and Nones :type array: list

Returns:the average of the list without the Nones.
Return type:float
simfleet.utils.chunk_path(path, speed_in_kmh)[source]

Splits the path into smaller chunks taking into account the speed.

Parameters:
  • path (list) – the original path. A list of points (lon, lat)
  • speed_in_kmh (float) – the speed in km per hour at which the path is being traveled.
Returns:

a new path equivalent (to the first one), that has at least the same number of points.

Return type:

list

simfleet.utils.load_class(class_path)[source]

Tricky method that imports a class form a string.

Parameters:class_path (str) – the path where the class to be imported is.
Returns:the class imported and ready to be instantiated.
Return type:class
simfleet.utils.request_path(agent, origin, destination, route_id)[source]

Sends a message to the RouteAgent to request a path

Parameters:
  • agent – the agent who is requesting the path
  • origin (list) – a list with the origin coordinates [longitude, latitude]
  • destination (list) – a list with the target coordinates [longitude, latitude]
Returns:

a list of points (longitude and latitude) representing the path,

the distance of the path in meters, a estimation of the duration of the path

Return type:

list, float, float

Examples

>>> path, distance, duration = request_path(agent, origin=[0,0], destination=[1,1])
>>> print(path)
[[0,0], [0,1], [1,1]]
>>> print(distance)
2.0
>>> print(duration)
3.24
simfleet.utils.status_to_str(status_code)[source]

Translates an int status code to a string that represents the status

Parameters:status_code (int) – the code of the status
Returns:the string that represents the status
Return type:str
simfleet.utils.unused_port(hostname)[source]

Return a port that is unused on the current host.

Module contents

Top-level package for SimFleet.

Contributing

Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

You can contribute in many ways:

Types of Contributions

Report Bugs

Report bugs at https://github.com/javipalanca/simfleet/issues.

If you are reporting a bug, please include:

  • Your operating system name and version.
  • Any details about your local setup that might be helpful in troubleshooting.
  • Detailed steps to reproduce the bug.

Fix Bugs

Look through the GitHub issues for bugs. Anything tagged with “bug” and “help wanted” is open to whoever wants to implement it.

Implement Features

Look through the GitHub issues for features. Anything tagged with “enhancement” and “help wanted” is open to whoever wants to implement it.

Write Documentation

SimFleet could always use more documentation, whether as part of the official SimFleet docs, in docstrings, or even on the web in blog posts, articles, and such.

Submit Feedback

The best way to send feedback is to file an issue at https://github.com/javipalanca/simfleet/issues.

If you are proposing a feature:

  • Explain in detail how it would work.
  • Keep the scope as narrow as possible, to make it easier to implement.
  • Remember that this is a volunteer-driven project, and that contributions are welcome :)

Get Started!

Ready to contribute? Here’s how to set up simfleet for local development.

  1. Fork the simfleet repo on GitHub.

  2. Clone your fork locally:

    $ git clone git@github.com:your_name_here/simfleet.git
    
  3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:

    $ mkvirtualenv simfleet
    $ cd simfleet/
    $ python setup.py develop
    
  4. Create a branch for local development:

    $ git checkout -b name-of-your-bugfix-or-feature
    

    Now you can make your changes locally.

  5. When you’re done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:

    $ flake8 simfleet tests
    $ python setup.py test or py.test
    $ tox
    

    To get flake8 and tox, just pip install them into your virtualenv.

  6. Commit your changes and push your branch to GitHub:

    $ git add .
    $ git commit -m "Your detailed description of your changes."
    $ git push origin name-of-your-bugfix-or-feature
    
  7. Submit a pull request through the GitHub website.

Pull Request Guidelines

Before you submit a pull request, check that it meets these guidelines:

  1. The pull request should include tests.
  2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst.
  3. The pull request should work for Python 3.6. Check https://travis-ci.org/javipalanca/simfleet/pull_requests and make sure that the tests pass for all supported Python versions.

Tips

To run a subset of tests:

$ py.test tests.test_simfleet

Credits

Development Lead

Contributors

  • Jaume Jordan

History

1.0.1 (2019-11-07)

  • SPADE and pandas version upgraded.
  • Stop simulation fixed.
  • Aesthetic changes.
  • Minor bug fixes.
  • Updated documentation.

1.0.0 (2019-11-05)

  • Moved from a taxi simulator to a generic fleet simulator.
  • Updated documentation.
  • Added support for different cities.
  • Directory agent now sends all the info.
  • Fixed bug of staying the corresponding time in the station when charging.
  • Changed logger to loguru library.
  • Removed fuel from transport popup, now is current_autonomy/max_autonomy.
  • Concurrent charging in stations now allowed through TimeoutBehavior.
  • Custom icons added.
  • Removed agents introduction from GUI.
  • Added specific parameters in scenario file (now config file).
  • CLI simplified.
  • Changed cli to config file.
  • Control of free places and status for StationAgent.
  • Fuel refill behavior between TransportAgent and StationAgent.
  • Refactoring from passenger to Customer
  • Refactoring from taxi to Transport
  • Refactoring from coordinator to fleet manager

0.4.1 (2019-01-07)

  • Fixed bug when checking if the simulation is finished.

0.4.0 (2018-10-25)

  • Improved the concurrent creation of agents.
  • Added stop and clear buttons to the interface.
  • Added download button for getting results in excel and json formats.
  • Documentation updated.

0.3.0 (2018-10-01)

  • Migrated to SPADE 3.
  • Documentation highly improved.
  • Helper functions added and refined.
  • Javascript framework included: VueJS
  • Routes centralized with a Route agent.
  • UI improved.

0.2 (2017-11-15)

  • Added scenario loading feature.

0.1.3 (2017-11-15)

  • Fixed minor bugs.

0.1.1 (2017-11-14)

  • Added documentation.

0.1.0 (2017-11-03)

  • First release on PyPI.

Indices and tables