// Handle frame buffer web socket requests (web socket is closed when we return) func FrameBufferHandler(ws *websocket.Conn) { // Not thread safe but good enough for debug output frameBufferListenerId++ src, done := framebuffer.AddListener("Virtual Frame Buffer "+strconv.Itoa(frameBufferListenerId), false) for { select { // src sends us frame buffer updates case fb := <-src: // Fails if the client has disappeared if err := sendFrameBufferToWebSocket(fb, ws); err != nil { log.Info("frameBufferSocketHandler " + err.Error()) // Un-subscribe before returning and closing connection done <- src return } } } }
// Monitors changes to frame buffer and turns power supplies on or off via USB relay board func relayDriver() { port := getPortName(relayPortMappings, 0) if port == "" { log.Warn("relayDriver unknown port name") return } // TODO: On Raspberry Pi if we unplug and replug the relay controller // before we notice (which can be minutes) a new USB port name is assigned // for the controller because the exiting one hasn't yet been closed. // i.e. /dev/ttyUSB0 -> /dev/ttyUSB1 This can't be fixed by updating the // controller even if it hasn't changed because the relay is quickly pulsed // off before setting the correct state connectLoop: for { relayUsbConnected = false f := openUsbPortWithRetry(port) relayUsbConnected = true // Initially we set all relays off relayStates := [2]relayState{} if err := initRelayBoard(f); err != nil { log.WithField("error", err.Error()).Warn("relayDriver failed to initialise relay board") continue connectLoop } // Request frame buffer updates src, done := framebuffer.AddListener(port, false) // Push frame buffer changes to Teensy pushLoop: for { select { case fb := <-src: now := time.Now() // foreach controller controllerCount := len(fb.Strips) / config.StripsPerTeensy for c := 0; c < controllerCount; c++ { firstStripForController := c * config.StripsPerTeensy // Should the controller's relay be off or on? relayStates[c].requestState( areAnyLedsOn(fb.Strips[firstStripForController:firstStripForController+config.StripsPerTeensy]), now) } // Work out the new actual states at this point in time newRelayStates := [2]bool{} updateRequired := false for i := 0; i < len(newRelayStates); i++ { updateRequired = relayStates[i].updateState(now) || updateRequired newRelayStates[i] = relayStates[i].current } // Update if changed if updateRequired { log.WithField("new states", newRelayStates).Info("relayDriver update relays") if err := sendRelayState(f, newRelayStates); err != nil { log.WithField("error", err.Error()).Warn("relayDriver failed to send relay command") f.Close() // Close down listener done <- src // Try and reconnect break pushLoop } } } } } }
// Monitors changes to frame buffer and update Teensy via USB func teensyDriver(driverIndex int) { port := getPortName(teensyPortMappings, driverIndex) if port == "" { log.WithField("driverIndex", driverIndex).Warn("teensyDriver unknown port name") return } for { // TODO: 2 of 2 Writing here causes race detector to fail teensyConnections[driverIndex] = false f := openUsbPortWithRetry(port) teensyConnections[driverIndex] = true // Allocate buffer once to avoid garbage collections in loop var data = make([]byte, 4+config.MaxLedStripLen*8*4+4) // Request frame buffer updates src, done := framebuffer.AddListener(port, true) // Push frame buffer changes to Teensy pushLoop: for { select { case fb := <-src: started := time.Now() // Build the frame buffer, start with header of 4 * 0xff i := 0 for z := 0; z < 4; z++ { data[i] = 0xff i++ } startStrip := driverIndex * 8 var checksum int32 = 0 // Buffer is send 8*LED1, 8*LED2 ... 8*(LEDS_PER_STRIP - 1) for l := 0; l < config.MaxLedStripLen; l++ { for s := startStrip; s < startStrip+8; s++ { if l >= len(fb.Strips[s].Leds) { // Pad frame buffer with zeros as strip is < MaxLedStripLen for z := 0; z < 4; z++ { data[i] = 0 i++ } } else { // Perform the output mapping here rgb := mapOutput(fb.Strips[s].Leds[l]) // Colours are sent as 4 bytes with leading 0x00 data[i] = 0 i++ data[i] = rgb.Red i++ data[i] = rgb.Green i++ data[i] = rgb.Blue i++ // Update the checksum checksum += ((int32(rgb.Red) << 16) + (int32(rgb.Green) << 8) + int32(rgb.Blue)) } } } // Append checksum MSB first for z := 3; z >= 0; z-- { data[i] = byte((checksum >> (8 * uint(z))) & 0xff) i++ } if _, err := f.Write(data); err != nil { log.WithField("error", err.Error()).Warn("teensyDriver send failed") f.Close() // Close down listener done <- src // Try and reconnect break pushLoop } stats.AddSerialSendTimeSample(port, time.Since(started)) } } } }