예제 #1
0
// 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
			}
		}
	}
}
예제 #2
0
// 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
					}
				}
			}
		}
	}
}
예제 #3
0
// 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))
			}
		}
	}
}