Esempio n. 1
0
    def run(self):
        track_log.info('Track Sim Starting...')
        track = Track()  # The track contains all it's devices and locos.

        # Start each track component-device's simulation thread
        # These devices exists "on" the track and simulate their own
        # operation.
        # TODO: Bases, Waysides, etc
        for l in track.locos.values():
            l.sim.start()

        # Update sim time multiplier if needed
        while True:
            try:
                time_iplier = self.timeq.get(timeout=.1)
                for l in track.locos.values():
                    l.sim.time_iplier = time_iplier
                track_log.info('Time Multiplier Set: ' + str(time_iplier))
            except Queue.Empty:
                pass

            # debug
            # for l in track.locos.values():
            #     status_str = 'Loco ' + l.ID + ': '
            #     status_str += str(l.speed) + ' @ ' + str(l.coords.marker)
            #     status_str += ' (' + str(l.coords.long) + ',' + str(l.coords.lat) + ')'
            #     status_str += '. Bases in range: '
            #     status_str += ', '.join([b.ID for b in l.bases_inrange])
            #     status_str += ' Conns: '
            #     status_str += ', '.join([c.conn_to.ID for c in l.conns.values() if c.conn_to])
            #     track_log.info(status_str)

            sleep(REFRESH_TIME)
Esempio n. 2
0
    def loco_messaging(loco):
        """ Real-time simulation of a locomotives's messaging system. Maintains
            connections to bases in range of loco's position. 
            # TODO: send/fetch msgs over them. 
            This function is intended to be run as a Thread.
        """
        while loco.sim.running:
            sleep(MSG_INTERVAL)  # Sleep for specified interval

            # Drop all out of range base connections and keep alive existing
            # in-range connections
            lconns = loco.conns.values()
            for conn in [c for c in lconns if c.connected() is True]:
                if conn.conn_to not in loco.bases_inrange:
                    conn.disconnect()
                else:
                    conn.keep_alive()

            open_conns = [c for c in lconns if c.connected() is False]
            used_bases = [c.conn_to for c in lconns if c.connected() is True]
            for i, conn in enumerate(open_conns):
                try:
                    if loco.bases_inrange[i] not in used_bases:
                        conn.connect(loco.bases_inrange[i])
                except IndexError:
                    break  # No (or no more) bases in range to consider

            # Ensure at least one active connection
            conns = [c for c in lconns if c.connected() is True]
            if not conns:
                err_str = ' skipping msg send/recv - No active comms.'
                track_log.warn(loco.name + err_str)
                continue  # Try again next iteration

            # Send status msg over active connections, breaking on first success.
            status_msg = get_6000_msg(loco)
            for conn in conns:
                try:
                    conn.send(status_msg)
                    info_str = ' - Sent status msg over ' + conn.conn_to.name
                    track_log.info(loco.name + info_str)
                except Exception as e:
                    track_log.warn(loco.name + ' send failed: ' + str(e))

            # Fetch incoming cad msgs over active connections, breaking on success.
            for conn in conns:
                cad_msg = None
                try:
                    cad_msg = conn.fetch(loco.emp_addr)
                except Queue.Empty:
                    break  # No msgs (or no more msgs) to receive.
                except Exception as e:
                    track_log.warn(loco.name + ' fetch failed: ' + str(e))
                    continue  # Try the next connecion

                # Process cad msg, if msg and if actually for this loco
                if cad_msg and cad_msg.payload.get('ID') == loco.ID:
                    try:
                        # TODO: Update track restrictions/loco locations
                        track_log.info(loco.name + ' - CAD msg processed.')
                    except:
                        track_log.error(loco.name +
                                        ' - Received invalid CAD msg.')
                    break  # Either way, the msg was fetched # TODO: ACK w/broker
            else:
                err_str = ' - active connections exist, but msg fetch/recv failed.'
                track_log.error(loco.name + err_str)
Esempio n. 3
0
    def loco_movement(loco):
        """ Real-time simulation of a locomotive's on-track movement. Also
            determines base stations in range of locos current position.
            This function is intended to be run as a Thread.
        """
        def _brake():
            """ Apply the adaptive braking algorithm.
            """
            raise NotImplementedError

        def _set_heading(prev_mp, curr_mp):
            """ Sets loco heading based on current and prev lat/long
            """
            lat1 = radians(prev_mp.lat)
            lat2 = radians(curr_mp.lat)

            long_diff = radians(prev_mp.long - curr_mp.long)

            a = cos(lat1) * sin(lat2)
            b = (sin(lat1) * cos(lat2) * cos(long_diff))
            x = sin(long_diff) * cos(lat2)
            y = a - b
            deg = degrees(atan2(x, y))
            compass_bearing = (deg + 360) % 360

            loco.heading = compass_bearing

        # Start
        makeup_dist = 0
        if not loco.direction or not loco.coords or loco.speed is None:
            raise ValueError('Cannot simulate an unintialized Locomotive.')

        while loco.sim.running:
            sleep(MSG_INTERVAL)  # Sleep for specified interval

            # Move, if at speed
            if loco.speed > 0:
                # Determine dist traveled since last iteration, including
                # makeup distance, if any.
                hours = REFRESH_TIME / 3600.0  # Seconds to hours, for mph
                hours = loco.sim.time_iplier * hours  # Apply sim time rate
                dist = loco.speed * hours * 1.0  # distance = speed * time
                dist += makeup_dist

                # Set sign of dist based on dir of travel
                if loco.direction == 'decreasing':
                    dist *= -1

                # Get next location and any makeup distance
                new_mp, dist = loco.track._get_next_mp(loco.coords, dist)

                # If no new_mp was returned, assume end of track
                if not new_mp:
                    err_str = ' - At end of track. Reversing.'
                    track_log.info(loco.name + err_str)

                    makeup_dist = 0
                    if loco.direction == 'decreasing':
                        loco.direction = 'increasing'
                    else:
                        loco.direction = 'decreasing'

                # Else update the loco accordingly
                else:
                    _set_heading(loco.coords, new_mp)
                    loco.coords = new_mp
                    makeup_dist = dist

                    # Determine base stations in range of current position
                    loco.bases_inrange = [
                        b for b in loco.track.bases.values()
                        if b.covers_location(loco.coords)
                    ]