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&eacute;lix G&oacute;mez M&aacute;rmol</a>, <a href="http://webs.um.es/gregorio" target="_blank">Gregorio Mart&iacute;nez P&eacute;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    }