Changeset - 9a81855ec766
[Not reviewed]
default
0 2 0
drewp@bigasterisk.com - 6 years ago 2019-06-08 07:43:46
drewp@bigasterisk.com
new artnet output module
Ignore-this: b86cd8e651b5848cf03c247123647505
2 files changed with 53 insertions and 6 deletions:
0 comments (0 inline, 0 general)
bin/collector
Show inline comments
 
@@ -18,25 +18,25 @@ import cyclone.web, cyclone.websocket
 
from greplin import scales
 

	
 
from cycloneerr import PrettyErrorHandler
 
from light9 import networking
 
from light9.collector.collector import Collector
 
from light9.collector.weblisteners import WebListeners
 
from greplin.scales.cyclonehandler import StatsHandler
 
from light9.namespaces import L9
 
from light9.zmqtransport import parseJsonMessage, startZmq
 
from rdfdb.syncedgraph import SyncedGraph
 
from standardservice.scalessetup import gatherProcessStats
 

	
 
from light9.collector.output import Udmx, DummyOutput  # noqa
 
from light9.collector.output import Udmx, ArtnetDmx, DummyOutput  # noqa
 

	
 

	
 
class Updates(cyclone.websocket.WebSocketHandler):
 

	
 
    def connectionMade(self, *args, **kwargs):
 
        log.info('socket connect %s', self)
 
        self.settings.listeners.addClient(self)
 

	
 
    def connectionLost(self, reason):
 
        self.settings.listeners.delClient(self)
 

	
 
    def messageReceived(self, message):
 
@@ -57,31 +57,31 @@ class Attrs(PrettyErrorHandler, cyclone.
 
        stats.setAttrFps.mark()
 
        with stats.setAttr.time():
 
            client, clientSession, settings, sendTime = parseJsonMessage(
 
                self.request.body)
 
            self.settings.collector.setAttrs(client, clientSession, settings,
 
                                             sendTime)
 
            self.set_status(202)
 

	
 

	
 
def launch(graph, doLoadTest=False):
 
    try:
 
        # todo: drive outputs with config files
 
        rate = 20 # On udmx, 22 breaks. 28 breaks. 30 breaks.
 
        rate = 20  # On udmx, 22 breaks. 28 breaks. 30 breaks.
 
        outputs = [
 
            
 
            Udmx(L9['output/dmxA/'], bus=3, address=None, lastDmxChannel=221, rate=rate),
 
            Udmx(L9['output/dmxB/'], bus=1, address=None, lastDmxChannel=221, rate=rate),
 

	
 
            #Udmx(L9['output/dmxB/'], bus=1, address=None, lastDmxChannel=221, rate=rate),
 
            #DummyOutput(L9['output/dmxA/']),
 
            DummyOutput(L9['output/dmxB/']),
 
            DummyOutput(L9['output/dmxA/']),
 
            ArtnetDmx(L9['output/dmxB/'], rate=rate),
 
        ]
 
    except Exception:
 
        log.error("setting up outputs:")
 
        traceback.print_exc()
 
        raise
 
    listeners = WebListeners()
 
    c: Collector = Collector(graph, outputs, listeners)
 

	
 
    startZmq(networking.collectorZmq.port, c)
 

	
 
    reactor.listenTCP(networking.collector.port,
 
                      cyclone.web.Application(handlers=[
light9/collector/output.py
Show inline comments
 
from rdflib import URIRef
 
import socket
 
import struct
 
import time
 
import usb.core
 
import logging
 
from twisted.internet import threads, reactor, task
 
from greplin import scales
 
log = logging.getLogger('output')
 
logAllDmx = logging.getLogger('output.allDmx')
 

	
 

	
 
class Output(object):
 
    """
 
    send a binary buffer of values to some output device. Call update
 
@@ -122,24 +124,69 @@ class FtdiDmx(BackgroundLoopOutput):
 
            # zeros there.
 
            buf = bytes([0]) + buf[:self.lastDmxChannel]
 

	
 
            if logAllDmx.isEnabledFor(logging.DEBUG):
 
                # for testing fps, smooth fades, etc
 
                logAllDmx.debug(
 
                    '%s: %s...' %
 
                    (self.shortId(), ' '.join(map(str, buf[:32]))))
 

	
 
            self.dmx.send_dmx(buf)
 

	
 

	
 
class ArtnetDmx(BackgroundLoopOutput):
 
    # adapted from https://github.com/spacemanspiff2007/PyArtNet/blob/master/pyartnet/artnet_node.py (gpl3)
 
    def __init__(self, uri, host, port, rate):
 
        """sends UDP messages to the given host/port"""
 
        super().__init__(uri, rate)
 
        packet = bytearray()
 
        packet.extend(map(ord, "Art-Net"))
 
        packet.append(0x00)  # Null terminate Art-Net
 
        packet.extend([0x00, 0x50])  # Opcode ArtDMX 0x5000 (Little endian)
 
        packet.extend([0x00, 0x0e])  # Protocol version 14
 
        self.base_packet = packet
 
        self.sequence_counter = 255
 
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 

	
 
    def _write(self, buf):
 
        self._writeStats.fps.mark()
 
        with self._writeStats.call.time():
 

	
 
            if not buf:
 
                logAllDmx.debug('%s: empty buf- no output', self.shortId())
 
                return
 

	
 
            if logAllDmx.isEnabledFor(logging.DEBUG):
 
                # for testing fps, smooth fades, etc
 
                logAllDmx.debug('%s: %s...' %
 
                                (self.shortId(), ' '.join(map(str, buf[:32]))))
 

	
 
            if self.sequence_counter:
 
                self.sequence_counter += 1
 
                if self.sequence_counter > 255:
 
                    self.sequence_counter = 1
 
            packet = self.base_packet[:]
 
            packet.append(self.sequence_counter)  # Sequence,
 
            packet.append(0x00)  # Physical
 
            universe_nr = 0
 
            packet.append(universe_nr & 0xFF)  # Universe LowByte
 
            packet.append(universe_nr >> 8 & 0xFF)  # Universe HighByte
 

	
 
            packet.extend(struct.pack(
 
                '>h', len(buf)))  # Pack the number of channels Big endian
 
            packet.extend(buf)
 

	
 
            self._socket.sendto(packet, ('127.0.0.1', 6454))
 

	
 

	
 
class Udmx(BackgroundLoopOutput):
 
    _reconnections = scales.IntStat('reconnections')
 
    _connected = scales.IntStat('connected')
 

	
 
    def __init__(self, uri, bus, address, lastDmxChannel, rate=22):
 
        self.bus = bus
 
        self.address = address
 
        self.lastDmxChannel = lastDmxChannel
 
        self.dev = None
 
        super().__init__(uri, rate=rate)
 

	
 
        self._errStats = scales.collection(self.statPath + '/write',
0 comments (0 inline, 0 general)