Changeset - 5633155c0300
[Not reviewed]
default
0 2 0
Drew Perttula - 6 years ago 2019-06-06 11:10:55
drewp@bigasterisk.com
sequencer doesn't hang so much, and it logs when you go from error->working
Ignore-this: 865392a34c76f0efaf18cb7f3d2c0c26
2 files changed with 47 insertions and 46 deletions:
0 comments (0 inline, 0 general)
light9/effect/sequencer.html
Show inline comments
 
@@ -28,7 +28,10 @@
 
         }
 
         td {
 
             white-space: nowrap;
 
             padding: 0 20px;
 
             padding: 0 10px;
 
             vertical-align: top;
 
             vertical-align: top;
 
             text-align: start;
 
         }
 
         tr.active { background: #151515; }
 
         .inactive > * { opacity: .5; }
 
@@ -57,16 +60,6 @@
 

	
 
        <h1>Sequencer <a href="stats/">[stats]</a></h1>
 

	
 
        <p>fps={{report.recentFps}}</p>
 
        
 
        <p>Recent frame times (best to worst): 
 
        <div class="chart">
 
         <template is="dom-repeat" items="{{report.recentDeltasStyle}}">
 
           <div style$="{{item}}"></div>
 
         </template>
 
        </div>
 
        </p>
 
        
 
        <h2>Song</h2>
 

	
 
        <resource-display graph="{{graph}}" uri="{{report.song}}"></resource-display>
 
@@ -92,10 +85,12 @@
 
              </td>  
 
              <td>
 
                <template is="dom-repeat" items="{{item.effectSettingsPairs}}">
 
                  <div>
 
                  <span class="effectSetting">
 
                    <resource-display graph="{{graph}}" uri="{{item.effectAttr}}"></resource-display>:
 
                    <span class="number">{{item.value}}</span>
 
                  </span>
 
                  </div>
 
                </template>
 
              </td>
 
              <td>
light9/effect/sequencer.py
Show inline comments
 
@@ -6,6 +6,7 @@ from louie import dispatcher
 
from rdflib import URIRef
 
from twisted.internet import reactor
 
from twisted.internet import defer
 
from twisted.internet.defer import Deferred, inlineCallbacks
 
from twisted.internet.inotify import INotify
 
from twisted.python.filepath import FilePath
 
import cyclone.sse
 
@@ -32,9 +33,8 @@ updateStats = scales.collection(
 
    '/update/',
 
    scales.PmfStat('s0_getMusic', recalcPeriod=1),
 
    scales.PmfStat('s1_eval', recalcPeriod=1),
 
    scales.PmfStat('s2_sendToWeb', recalcPeriod=1),
 
    #scales.PmfStat('s3_send_client', recalcPeriod=1),
 
    scales.PmfStat('s3_send', recalcPeriod=1),
 
    scales.PmfStat('sendPhase', recalcPeriod=1),
 
    scales.PmfStat('updateLoopLatency', recalcPeriod=1),
 
    scales.DoubleStat('updateLoopLatencyGoal'),
 
    scales.RecentFpsStat('updateFps'),
 
@@ -160,7 +160,7 @@ class Sequencer(object):
 

	
 
    def __init__(self,
 
                 graph: SyncedGraph,
 
                 sendToCollector: Callable[[DeviceSettings], defer.Deferred],
 
                 sendToCollector: Callable[[DeviceSettings], Deferred],
 
                 fps=40):
 
        self.graph = graph
 
        self.fps = fps
 
@@ -175,10 +175,15 @@ class Sequencer(object):
 
        self.notes: Dict[Song, List[Note]] = {}  # song: [notes]
 
        self.simpleOutputs = SimpleOutputs(self.graph)
 
        self.graph.addHandler(self.compileGraph)
 
        self.lastLoopSucceeded = False
 

	
 
        self.codeWatcher = CodeWatcher(onChange=self.onCodeChange)
 
        self.updateLoop()
 

	
 
        self.codeWatcher = CodeWatcher(
 
            onChange=lambda: self.graph.addHandler(self.compileGraph))
 
    def onCodeChange(self):
 
        log.debug('seq.onCodeChange')
 
        self.graph.addHandler(self.compileGraph)
 
        #self.updateLoop()
 

	
 
    @compileStats.graph.time()
 
    def compileGraph(self) -> None:
 
@@ -192,43 +197,43 @@ class Sequencer(object):
 

	
 
    @compileStats.song.time()
 
    def compileSong(self, song: Song) -> None:
 
        anyErrors = False
 
        self.notes[song] = []
 
        for note in self.graph.objects(song, L9['note']):
 
            self.notes[song].append(
 
                Note(self.graph, NoteUri(note), effecteval, self.simpleOutputs))
 
            try:
 
                n = Note(self.graph, NoteUri(note), effecteval, self.simpleOutputs)
 
            except Exception:
 
                log.warn(f"failed to build Note {note} - skipping")
 
                anyErrors = True
 
                continue
 
            self.notes[song].append(n)
 
        if not anyErrors:
 
            log.info('built all notes')
 

	
 
    @inlineCallbacks
 
    def updateLoop(self) -> None:
 
        frameStart = time.time()
 

	
 
        d = self.update()
 
        sendStarted = time.time()
 

	
 
        def done(sec: float):
 
        try:
 
            sec = yield self.update()
 
        except Exception as e:
 
            self.lastLoopSucceeded = False
 
            traceback.print_exc()
 
            log.warn('updateLoop: %r', e)
 
            reactor.callLater(1, self.updateLoop)
 
        else:
 
            took = time.time() - frameStart
 
            delay = max(0, 1 / self.fps - took)
 
            updateStats.updateLoopLatency = took
 

	
 
            # time to send to collector, reported by collector_client
 
            if isinstance(
 
                    sec,
 
                    float):  # sometimes None, not sure why, and neither is mypy
 
                updateStats.s3_send = sec
 
            if not self.lastLoopSucceeded:
 
                log.info('Sequencer.update is working')
 
                self.lastLoopSucceeded = True
 

	
 
            # time to send to collector, measured in this function,
 
            # from after sendToCollector returned its deferred until
 
            # when the deferred was called.
 
            updateStats.sendPhase = time.time() - sendStarted
 
            delay = max(0, 1 / self.fps - took)
 
            reactor.callLater(delay, self.updateLoop)
 

	
 
        def err(e):
 
            log.warn('updateLoop: %r', e)
 
            reactor.callLater(2, self.updateLoop)
 

	
 
        d.addCallbacks(done, err)
 

	
 
    @updateStats.updateFps.rate()
 
    def update(self) -> defer.Deferred:
 
        try:
 
    @inlineCallbacks
 
    def update(self) -> Deferred:
 
            with updateStats.s0_getMusic.time():
 
                musicState = self.music.getLatest()
 
                if not musicState.get('song') or not isinstance(
 
@@ -252,14 +257,15 @@ class Sequencer(object):
 
                    settings.append(s)
 
                devSettings = DeviceSettings.fromList(self.graph, settings)
 

	
 
            with updateStats.s2_sendToWeb.time():
 
                dispatcher.send('state', update={'songNotes': noteReports})
 

	
 
            return self.sendToCollector(devSettings)
 
        except Exception:
 
            traceback.print_exc()
 
            raise
 
        with updateStats.s3_send.time(): # our measurement
 
            sendSecs = yield self.sendToCollector(devSettings)
 

	
 
        # sendToCollector's own measurement.
 
        # (sometimes it's None, not sure why, and neither is mypy)
 
        #if isinstance(sendSecs, float):
 
        #    updateStats.s3_send_client = sendSecs
 

	
 
class Updates(cyclone.sse.SSEHandler):
 

	
0 comments (0 inline, 0 general)