001 /**
002 * "TRMSim-WSN, Trust and Reputation Models Simulator for Wireless
003 * Sensor Networks" is free software: you can redistribute it and/or
004 * modify it under the terms of the GNU Lesser General Public License
005 * as published by the Free Software Foundation, either version 3 of
006 * the License, or (at your option) any later version always keeping
007 * the additional terms specified in this license.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012 * GNU Lesser General Public License for more details.
013 *
014 *
015 * Additional Terms of this License
016 * --------------------------------
017 *
018 * 1. It is Required the preservation of specified reasonable legal notices
019 * and author attributions in that material and in the Appropriate Legal
020 * Notices displayed by works containing it.
021 *
022 * 2. It is limited the use for publicity purposes of names of licensors or
023 * authors of the material.
024 *
025 * 3. It is Required indemnification of licensors and authors of that material
026 * by anyone who conveys the material (or modified versions of it) with
027 * contractual assumptions of liability to the recipient, for any liability
028 * that these contractual assumptions directly impose on those licensors
029 * and authors.
030 *
031 * 4. It is Prohibited misrepresentation of the origin of that material, and it is
032 * required that modified versions of such material be marked in reasonable
033 * ways as different from the original version.
034 *
035 * 5. It is Declined to grant rights under trademark law for use of some trade
036 * names, trademarks, or service marks.
037 *
038 * You should have received a copy of the GNU Lesser General Public License
039 * along with this program (lgpl.txt). If not, see <http://www.gnu.org/licenses/>
040 */
041
042 package es.ants.felixgm.trmsim_wsn.network;
043
044 import es.ants.felixgm.trmsim_wsn.search.ISearchCondition;
045 import es.ants.felixgm.trmsim_wsn.search.IsServerSearchCondition;
046 import es.ants.felixgm.trmsim_wsn.trm.GatheredInformation;
047 import es.ants.felixgm.trmsim_wsn.trm.TRModel_WSN;
048 import es.ants.felixgm.trmsim_wsn.outcomes.Outcome;
049 import es.ants.felixgm.trmsim_wsn.search.IsSensorSearchCondition;
050
051 import java.util.Collection;
052 import java.util.ArrayList;
053 import java.util.HashMap;
054 import java.util.Hashtable;
055 import java.util.Iterator;
056 import java.util.LinkedList;
057 import java.util.Timer;
058 import java.util.TimerTask;
059 import java.util.Vector;
060
061 /**
062 * <p>This class models a sensor in a Wireless Sensor Network</p>
063 <font color="#FF0000">
064 <p><strong>A subclass of this class, modeling the specific sensors, has to be
065 implemented in order to add a new Trust and Reputation Model</strong></p>
066 </font>
067 * @author <a href="http://ants.dif.um.es/~felixgm/en" target="_blank">Félix Gómez Mármol</a>, <a href="http://webs.um.es/gregorio" target="_blank">Gregorio Martínez Pérez</a>
068 * @version 0.5
069 * @since 0.1
070 */
071 public abstract class Sensor implements Runnable {
072 /** Indicates whether a collusion among malicious sensors is built */
073 protected static boolean collusion = false;
074 /** Indicates whether some sensors will switch off sometimes in order to save energy */
075 protected static boolean dynamic = false;
076 /** Indicates whether a simulation is currently running or not */
077 protected static boolean runningSimulation = false;
078 /** Sensors' identifier counter */
079 protected static int idCount = 1;
080 /** Sensor's identifier */
081 protected int id;
082 /** Maximum distance between two nodes in the network */
083 protected static double _maxDistance = 0;
084 /** X coordinate of this sensor */
085 protected double xPosition;
086 /** Y coordinate of this sensor */
087 protected double yPosition;
088 /** Outcoming links of this sensor */
089 protected Collection<Link> links;
090 /** Goodness of this sensor related to each provided service */
091 protected HashMap<Service,Double> servicesGoodness;
092 /** Service requested by the clients */
093 protected Service requiredService;
094 /** Indicates whether this sensor is currently active or in an idle state */
095 protected boolean activeState;
096 /** Number of service requests provided by this sensor */
097 protected int numRequests;
098 /** Number of service requests provided by this sensor after which it goes to sleep */
099 protected static final int numRequestsThreshold = 20;
100 /** Used to determine the amount of time (in ms) a sensor stays asleep */
101 private static final long sleepingTimeoutMilis = 1000;
102 /** Current Trust and Reputation model used by every Sensor */
103 protected static TRModel_WSN trmmodelWSN;
104 /** Last outcome of a performed transaction */
105 protected Outcome outcome;
106 /** Total distance traveled by the messages sent from this sensor */
107 protected long transmittedDistance;
108 /** Three timers that are used to set the timing for sleep/active state */
109 protected Timer numRequestsTimer;
110 protected Timer sleepTimer;
111 protected Timer sleepTimerAux;
112
113 /**
114 * Class Sensor constructor.
115 * Creates a new Sensor and locates it randomly
116 */
117 public Sensor() {
118 this(idCount++,Math.random()*_maxDistance,Math.random()*_maxDistance);
119 }
120
121 /**
122 * Class Sensor constructor
123 * @param id Sensor's identifier
124 * @param x X coordinate of this sensor
125 * @param y Y coordinate of this sensor
126 */
127 public Sensor(int id, double x, double y) {
128 this.id = id;
129 links = new ArrayList<Link>();
130 xPosition = x;
131 yPosition = y;
132 activeState = true;
133 transmittedDistance = 0;
134
135 servicesGoodness = new HashMap<Service,Double>();
136 numRequests = 0;
137 // Added by Hamed Khiabani
138 numRequestsTimer = null;
139 sleepTimer = null;
140 sleepTimerAux = null;
141 sleepIfInactive(sleepingTimeoutMilis/2+((int)(Math.random()*(sleepingTimeoutMilis/2))));
142 }
143
144 /**
145 <p>This method uses the current trust and reputation model in order to find
146 the most trustworthy server offering the required service. It requests that
147 service to the server found and punishes or rewards it according to its
148 satisfaction with the received service</p>
149 */
150 public void run() {
151 if (reachesQualifiedService(requiredService)) {
152 GatheredInformation gi = trmmodelWSN.gatherInformation(this, requiredService);
153 Vector<Sensor> path = trmmodelWSN.scoreAndRanking(this,gi);
154 outcome = trmmodelWSN.performTransaction(path,requiredService);
155 if (outcome != null) {
156 if (outcome.get_satisfaction().isSatisfied())
157 outcome = trmmodelWSN.reward(path,outcome);
158 else
159 outcome = trmmodelWSN.punish(path,outcome);
160 }
161 } else
162 outcome = null;
163 }
164
165 /**
166 * This method returns a requested service. The similarity between the actually
167 * offered service and the actually delivered service depends on the goodness of
168 * this sensor about this service. If it has a high goodness both services will be
169 * very similar (or equal), otherwise it will deliver a very different service than the
170 * one requested
171 * @param service Requested service
172 * @param path Path leading from the client requesting the service to the server providing it
173 * @return A service more or less similar to the requested one, depending on the goodness
174 * of this sensor delivering that certain service
175 */
176 public Service serve(Service service, Vector<Sensor> path) {
177 Service givenService = service.clone();
178 try {
179 if (get_goodness(service) < 0.5)
180 givenService = null;
181 } catch (Exception ex) {
182 givenService = null;
183 }
184
185 for (int i = 0; i < path.size()-1; i++)
186 transmittedDistance += path.get(i).distance(path.get(i+1));
187
188 numRequests++;
189 if (numRequests == numRequestsThreshold) { // Edited by Hamed Khiabani
190 numRequests = 0;
191 if (dynamic && runningSimulation) {
192 activeState = false;
193 numRequestsTimer = new Timer();
194 numRequestsTimer.schedule(new TimerTask(){
195 @Override
196 public void run() {
197 activeState = true;
198 numRequestsTimer.cancel();
199 }
200 },sleepingTimeoutMilis);
201 }
202 }
203
204 return givenService;
205 }
206
207 /**
208 * Indicates if this sensor can reach another sensor offering a given service with posiitve goodness
209 * @param service The requested service
210 * @return true if this sensor can reach another sensor offering a given service with posiitve goodness; false otherwise
211 */
212 private boolean reachesQualifiedService(Service service) {
213 Collection<Vector<Sensor>> pathsToServers = findSensors(new IsServerSearchCondition(service,IsServerSearchCondition.BENEVOLENT_SERVER));
214 return ((pathsToServers != null) && (pathsToServers.size() > 0));
215 }
216
217 /**
218 * Adds a new service to the set of offered services of this sensor
219 * @param service The new service to be added
220 * @param goodness The goodness when offering that new service
221 */
222 public void addService(Service service, double goodness) {
223 servicesGoodness.put(service,new Double(goodness));
224 }
225
226 /**
227 * Removes a service from the set of offered services by this sensor
228 * @param service Service to be removed from the set of offered services by this sensor
229 */
230 public void removeService(Service service) {
231 servicesGoodness.remove(service);
232 if (requiredService.equals(service))
233 requiredService = null;
234 }
235
236 /**
237 * Gets the goodness of a given service
238 * @param service The service to get its goodness
239 * @return The goodness of the given service
240 * @throws Exception If this sensor does not offer the given service
241 */
242 public double get_goodness(Service service) throws Exception {
243 if (!offersService(service.id()))
244 throw new Exception("Server "+id+" doesn't offer service "+service.id());
245 return servicesGoodness.get(getService(service.id())).doubleValue();
246 }
247
248 /**
249 * Sets the goodness of a given service
250 * @param service The service to set its goodness
251 * @param goodness The goodness to be set
252 * @throws Exception If this sensor does not offer the given service
253 */
254 public void set_goodness(Service service, double goodness) throws Exception {
255 if (!offersService(service.id()))
256 throw new Exception("Server "+id+" doesn't offer service "+service.id());
257
258 servicesGoodness.put(service, new Double(goodness));
259 }
260
261 /**
262 * Returns the number of services this sensor provides
263 * @return Number of services
264 */
265 public int get_numServices() {
266 if (servicesGoodness == null)
267 return 0;
268 return servicesGoodness.keySet().size();
269 }
270
271 /**
272 * Returns a collection with the services this sensor provides
273 * @return Collection of services.
274 */
275 public Collection<Service> get_services() {
276 if (servicesGoodness == null)
277 return null;
278 return servicesGoodness.keySet();
279 }
280
281 /**
282 * Returns the requested service or null if this sensor does not offer such service
283 * @param service The id of the service to be retrieved
284 * @return The requested service or null if this sensor does not offer such service
285 */
286 public Service getService(String service) {
287 if (servicesGoodness != null) {
288 Iterator<Service> serviceIt = servicesGoodness.keySet().iterator();
289 while (serviceIt.hasNext()) {
290 Service service1 = serviceIt.next();
291 if (service1.id().equalsIgnoreCase(service))
292 return service1;
293 }
294 }
295 return null;
296 }
297
298 /**
299 * This method finds the shortest paths from this Sensor to any reachable
300 * Sensor satisfying a given condition (inactive or idle sensors are excluded from any path)
301 * @param searchCondition Condition to be accomplished by a sensor in order to consider it "reachable"
302 * @return Paths from this Sensor to any reachable sensor satisfying a given condition
303 */
304 public Collection<Vector<Sensor>> findSensors(ISearchCondition searchCondition) {
305 Collection<Vector<Sensor>> out = new LinkedList<Vector<Sensor>>();
306 Collection<Sensor> Q = new ArrayList<Sensor>(); // All nodes in the graph are unoptimized - thus are in Q
307 Collection<Sensor> visitedNodes = new ArrayList<Sensor>(); // Visited nodes
308 Hashtable<Sensor,Double> distanceFromSource = new Hashtable<Sensor,Double>(); // Distance from source
309 Hashtable<Sensor,Sensor> previousNode = new Hashtable<Sensor,Sensor>(); // Previous node in optimal path from source
310
311 distanceFromSource.put(this, 0.0); // Distance from source to source
312 previousNode.put(this, this);
313 Q.add(this);
314
315 while (!Q.isEmpty()) { // The main loop
316 double minD = Double.POSITIVE_INFINITY;
317 Sensor closestNode = null;
318 for (Sensor sensor : Q) //closestNode := vertex in Q with smallest dist[]
319 if (distanceFromSource.get(sensor) < minD) {
320 minD = distanceFromSource.get(sensor);
321 closestNode = sensor;
322 }
323
324 Q.remove(closestNode);
325 visitedNodes.add(closestNode);
326 for (Sensor sensor : closestNode.getNeighbors()) { // where sensor has not yet been removed from Q.
327 if ((!Q.contains(sensor)) && (!visitedNodes.contains(sensor))){
328 distanceFromSource.put(sensor, Double.POSITIVE_INFINITY);
329 Q.add(sensor);
330 }
331
332 double alternative = distanceFromSource.get(closestNode) + closestNode.distance(sensor);
333 if (alternative < distanceFromSource.get(sensor)) {
334 distanceFromSource.put(sensor, alternative);
335 previousNode.put(sensor, closestNode);
336 }
337 }
338 }
339
340 for (Sensor sensor : previousNode.keySet()) {
341 if ((sensor.id() != id) && sensor.isActive() && (searchCondition.sensorAcomplishesCondition(sensor))) {
342 boolean intermediateInactiveSensor = false;
343 LinkedList<Sensor> path1 = new LinkedList<Sensor>();
344 path1.addFirst(sensor);
345 Sensor prev = sensor;
346 while (prev != this){
347 prev = previousNode.get(prev);
348 if (!prev.isActive())
349 intermediateInactiveSensor = true;
350 path1.addFirst(prev);
351 }
352 Vector<Sensor> path2 = new Vector<Sensor>();
353 for (Sensor s1 : path1)
354 path2.add(s1);
355
356 if (!intermediateInactiveSensor)
357 out.add(path2);
358 }
359 }
360
361 return out;
362 }
363
364 /**
365 * Indicates this sensor offers a certain service or not
366 * @param service The asking service
367 * @return true if this sensor indeed offers the asking service, false otherwise
368 */
369 public boolean offersService(Service service) {
370 return offersService(service.id());
371 }
372
373 /**
374 * Indicates if a sensor offers a certain service or not
375 * @param service The asking service's name
376 * @return true if this sensor indeed offers the asking service, false otherwise
377 */
378 protected boolean offersService(String service) {
379 return (getService(service) != null);
380 }
381
382
383 /**
384 * Returns all the neighbors of this sensor, i.e., all the sensors reachable from
385 * this one, one step forward
386 * @return Neighbor sensors of this sensor
387 */
388 public Collection<Sensor> getNeighbors() {
389 Collection<Sensor> neighbors = new ArrayList<Sensor>();
390
391 for (Link link : links) { // We add the destination of all the links
392 neighbors.add(link.get_destination());
393 transmittedDistance += distance(link.get_destination());
394 }
395 return neighbors;
396 }
397
398 /**
399 * Deletes all the neighbors of this sensor
400 */
401 public void removeAllNeighbors() {
402 links = new ArrayList<Link>();
403 }
404
405 /**
406 * Indicates if a given sensor is neighbor of this one
407 * @param sensor The asking sensor
408 * @return true if the specified sensor is neighbor of this sensor, false otherwise
409 */
410 public boolean isNeighbor(Sensor sensor) {
411 if (links != null)
412 for (Link link : links) {
413 if (link.get_destination().equals(sensor))
414 return true;
415 }
416 return false;
417 }
418
419 /**
420 * Adds a link to a given sensor
421 * @param sensor Sensor to link to
422 */
423 public void addLink(Sensor sensor) {
424 if (!isNeighbor(sensor)) {
425 if (links == null)
426 links = new ArrayList<Link>();
427
428 Link link = new Link(this,sensor);
429 links.add(link);
430 }
431 }
432
433 /**
434 * Removes the link with a given sensor
435 * @param sensor Sensor to remove link with
436 */
437 public void removeLink(Sensor sensor) {
438 Iterator<Link> linkIt = links.iterator();
439 while (linkIt.hasNext())
440 {
441 Link link = linkIt.next();
442 if (link.get_destination().equals(sensor))
443 linkIt.remove();
444 }
445 }
446
447 /**
448 * Calculates the distance between this sensor and a given one
449 * @param sensor Sensor to calculate the distance from
450 * @return The distance between this sensor and the given one
451 */
452 public double distance(Sensor sensor) {
453 return Math.sqrt(Math.pow(xPosition-sensor.getX(),2)+Math.pow(yPosition-sensor.getY(),2));
454 }
455
456 /**
457 * This method returns the minimum number of hops from this sensor to the
458 * specified one
459 * @param sensor Sensor whose distance from this sensor in number of hops
460 * is requested
461 * @return The minimum number of hops from this sensor to the specified one
462 */
463 public int distanceInHops(Sensor sensor) {
464 int distanceInHops = Integer.MAX_VALUE;
465 Collection<Vector<Sensor>> pathsToSensor = findSensors(new IsSensorSearchCondition(sensor.id()));
466
467 if (pathsToSensor != null)
468 for (Vector<Sensor> pathToSensor : pathsToSensor)
469 if (pathToSensor.size() < distanceInHops)
470 distanceInHops = pathToSensor.size();
471
472 return distanceInHops;
473 }
474
475 /**
476 * Makes this sensor to go to sleep if it is inactive for a period of time
477 * @param time Amount of time this sensor remains asleep
478 * Edited by Hamed Khiabani
479 */
480 private void sleepIfInactive(final long time) {
481 if (dynamic && runningSimulation) {
482 sleepTimer = new Timer();
483 sleepTimer.schedule(new TimerTask() {
484 @Override
485 public void run() {
486 if (numRequests < numRequestsThreshold / 2) {
487 activeState = false;
488 sleepTimerAux = new Timer();
489 sleepTimerAux.schedule(new TimerTask() {
490 @Override
491 public void run() {
492 activeState = true;
493 sleepTimerAux.cancel();
494 }
495 }, time /2);
496 }
497 sleepTimer.cancel();
498 }
499 }, time, time);
500 }
501 }
502
503
504 /**
505 * Returns sensor's id.
506 * @return Sensor's id
507 */
508 public int id() { return id; }
509
510 /**
511 * Returns the X coordinate
512 * @return Coordinate X
513 */
514 public double getX() { return xPosition; }
515
516 /**
517 * Returns the Y coordinate
518 * @return Coordinate Y
519 */
520 public double getY() { return yPosition; }
521
522 /**
523 * Returns the service required by this client
524 * @return The service required by this client
525 */
526 public Service get_requiredService() { return requiredService; }
527
528 /**
529 * Returns the total distance traveled by the messages sent from this sensor
530 * @return The total distance traveled by the messages sent from this sensor
531 */
532 public long get_transmittedDistance() { return transmittedDistance; }
533
534 /**
535 * This method returns a boolean indicating whether there is currently a simulation running or not
536 * @return Boolean indicating whether there is currently a simulation running or not
537 */
538 public static boolean isRunningSimulation() { return runningSimulation; }
539
540 /**
541 * Returns Current Trust and Reputation model used by every Sensor
542 * @return Current Trust and Reputation model used by every Sensor
543 */
544 public static TRModel_WSN get_TRModel_WSN() { return trmmodelWSN; }
545
546
547 /**
548 * Returns the last outcome of a performed transaction
549 * @return The last outcome of a performed transaction
550 */
551 public Outcome get_outcome() { return outcome; }
552
553 /**
554 * This method updates the state of this sensor
555 * @param active_state New state of this sensor: true if the new state is active, and false otherwise
556 */
557 public void setActiveState(boolean active_state) { this.activeState = active_state; }
558
559
560 /**
561 * Updates the collusion value.
562 * @param coll New collusion value.
563 */
564 public static void setCollusion(boolean coll) {collusion = coll; }
565
566 /**
567 * Updates the dynamic value.
568 * @param dyn New dynamic value.
569 */
570 public static void setDynamic(boolean dyn) { dynamic = dyn; }
571
572 /**
573 * Updates the runningSimulation attribute.
574 * @param _runningSimulation New runningSimulation value.
575 */
576 public static void setRunningSimulation(boolean _runningSimulation) { runningSimulation = _runningSimulation; }
577
578 /**
579 * This method sets the maximum distance between two nodes in the network
580 * @param maxDistance Maximum distance between two nodes in the network
581 */
582 public static void setMaxDistance(double maxDistance) { _maxDistance = maxDistance; }
583
584 /**
585 * Updates the client required Service.
586 * @param requiredService New service.
587 */
588 public void set_requiredService(Service requiredService) { this.requiredService = requiredService; }
589
590 /**
591 * This method sets the current Trust and Reputation model used by every Sensor
592 * @param TRModel_WSN New Trust and Reputation model used by every Sensor
593 */
594 public static void set_TRModel_WSN(TRModel_WSN TRModel_WSN) { trmmodelWSN = TRModel_WSN; }
595
596 /**
597 * Indicates if this sensor is active or not
598 * @return true if this sensor is active, false otherwise
599 */
600 public boolean isActive() { return activeState;}
601
602 /**
603 * This method increases the total distance traveled by the messages sent from this sensor
604 * @param distance Amount of distance to be added
605 */
606 public void addTransmittedDistance(long distance) { transmittedDistance += distance; }
607
608 /**
609 * Resets the identifier counter to 1
610 */
611 public static void resetId() { idCount = 1; }
612
613 /**
614 * It indicates if this sensor is equal to a certain one, according to their id
615 * @param node Sensor to check its equality
616 * @return true if this sensor is equal to the specified one, false otherwise
617 */
618 public boolean equals(Sensor node) {
619 return (id == node.id());
620 }
621
622 /**
623 * This method resets a Sensor to its initial state
624 */
625 public abstract void reset();
626
627 /**
628 * This method returns a String representation of this sensor
629 * @return A String representation of this sensor
630 */
631 @Override
632 public String toString()
633 {
634 String s = id+" ("+((int)(xPosition*100))/100.0+","+((int)(yPosition*100))/100.0+") ->";
635
636 for (Link link : links)
637 s += " "+link.get_destination().id();
638 return s;
639 }
640
641 /**
642 * This method cancels and purges the sensor timers
643 * Added by Hamed Khiabani
644 */
645 public void cancelAllTimers() {
646 activeState = true;
647
648 if(numRequestsTimer != null){
649 numRequestsTimer.cancel();
650 numRequestsTimer.purge();
651 }
652 if(sleepTimer != null){
653 sleepTimer.cancel();
654 sleepTimer.purge();
655 }
656 if(sleepTimerAux != null){
657 sleepTimerAux.cancel();
658 sleepTimerAux.purge();
659 }
660 }
661 }