Code Monkey home page Code Monkey logo

daliserver's People

Contributors

onitake avatar philippstroehle avatar rnixx avatar s3lph avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

daliserver's Issues

Tridonic BM RS-232 with USB adapter

Hello,
First of all, thank you for maintaining this awesome library. It really is great. I've just got one small problem.

I'm using a Tridonic BM RS-232 (which is is usually through serial) plugged into a RS-232 to USB adapter in hopes of using this library. Unfortunately, the daliserver library can't seem to find it no matter what I try.

However, the official DALI Tool finds it and operates it perfectly fine. I'm just curious if you have any input or advice on this. Thanks!

pack/unpack bug

in pack.c in pack/unpack functions in. PACK_UBYTE/PACK_BYTE case u need to define "value" as a 1 byte variable e.g.:
unsigned char value = va_arg(args, unsigned int); instead of
unsigned int value = va_arg(args, unsigned int);
Same applies for PACK_USHORT (unsinged short value = ...)
In those cases u write 4 bytes instead of 1/2 in each of fields of "UsbDaliIn in;" structure.. For PACK_UBYTE on little endian architectures LSB is 1st byte so it works "ok", with zeroing additional 3 bytes, but on big endian architecture u actually overwrite 4th byte with usable data, while zeroing first 3..

Otherwise, awesome work!

Regards,
Goran

Not handling unknown message type 0x77

I'm writing a script that finds ballasts on a DALI network using INITIALISE / RANDOMISE / COMPARE / WITHDRAW / TERMINATE etc. as a test case for a library I'm writing. See https://github.com/sde1000/python-dali - the script in question is "find-ballasts.py".

When the COMPARE command causes several ballasts to reply YES (0xff) simultaneously, daliserver logs the message "Not handling unknown message type 0x77" and then a second later "ERROR Error sending DALI message: Receive timeout". It returns status code 255 over the network.

If I hack my script to interpret status code 255 as "YES" then the script runs as expected.

I believe message type 0x77 is the hardware indicating "response present but garbled". It would be useful to handle this and return some appropriate status code over the network.

Cannot connect to daliserver

Installed on a raspberry pi 3, as per instructions. Everything seemed fine, and the service is running. But can't seem to connect. I've tried everything I can think of for ip address and port, and keep getting messages like:
Can't connect to 127.0.0.1:55825 at usbdali.pm line 72

Using a debugger, the low-level connect function in IO/Socket.pm is saying "Connection refused".

Your help will be greatly appreciated!!
Thanks

usb error

when I try to run daliserver on debian64 compiled from git clone i get:
DALI USB multiplexer (daliserver)
Copyright (c) 2011 onitake All rights reserved.

[2016-05-08 12:44:11] INFO Starting daliserver
[2016-05-08 12:44:11] ERROR Error opening USB device: Access error
[2016-05-08 12:44:11] ERROR Can't find USB device
[2016-05-08 12:44:11] INFO Exiting

The DALI usb is working on the windows side on DaliCopckit ,

On lusb
the dali usb from tridonic is listed as :
< Bus 003 Device 008: ID 17b5:0020

Thank's for reading

Error sending DALI message: Send timeout

When we boot our system, at first everything works fine.
However after not sending any commands to DALI at night, when we start sending them again the next day, we see this in the logs :
Sep 14 18:59:33 dex daliserver: Error sending DALI message: Send timeout

Restarting daliserver doesn't help. Only restarting the machine works.
Any ideas ?

daliserver in VM not working

Hi,

I tried to setup daliserver in a ubuntu 14.04 guest on a windows 10 host with virtualbox. The USB device filter for the VM is configured properly to access Lunatone DALI USB through guest system.

When trying to start daliserver with "/etc/init.d/daliserver start", following gets written to "/var/log/daliserver.log":

[2016-00-04 13:17:10] INFO Starting daliserver
[2016-00-04 13:17:15] **ERROR** Error setting configuration: Other error
[2016-00-04 13:17:50] **ERROR** Error reattaching interface: Not found

Any hints how I can solve this?

Can't tell whether there was a response to a command

The network protocol for daliserver doesn't allow it to indicate whether or not a command returned a response. Lack of response to a command is reported as response 0xff, status 0, which is the same as a response of 0xff.

This makes some DALI commands impossible to use. For example, I can happily use the "QUERY ACTUAL LEVEL" command and receive valid data back because this command always causes a response from the ballast. I can't use the "QUERY LAMP FAILURE" command because this command causes a response of 0xff for "yes" and a lack of response for "no" — daliserver squashes both of these to a response of 0xff.

In lib/usb.c you distinguish between the two cases, but you just call dali->req_callback() with a hard-coded 0xff when you're dealing with USBDALI_TYPE_NO_RESPONSE; a fix for this ought to be fairly simple. The problem (and the reason I'm not sending a patch) is that the network protocol will have to be changed to fix this and I don't want to pre-empt whatever change you decide to make.

Updated Mas OS kext

The dummy kext wasn't allowing me to claim the device on Mac OS El Capitan 10.11.2.

I updated the plist with kpi libraries but -- in the end -- it was essential to declare IOProviderClass as IOUSBDevice and not IOUSBInterface. Please note the libkern library could likely be sent back a few versions. Importantly, the attached is unsigned so SIP must be disabled to use it.

Info.plist.txt

Package source code archive

To build the source code, a complete autotools suite is currently needed. This can be a bit tricky to install on certain platforms (i.e. Mac OS X).

Package the source code with prebuilt configure and automake scripts for release versions.

ERROR: Cant find USB Device

Hi.
I followed the debian installation instruction and when starting the daliserver it throws me an error saying it cannot find a usb device. And I can see that my /dev has no /dali device, instead upon plugging in the device it turns up as hiddev1 in my dev/usb folder. As I am new to this can you please guide me and help me debug this error?

Error:
DALI USB multiplexer (daliserver)
Copyright (c) 2011 onitake All rights reserved.

[2021-06-25 12:40:16] INFO Starting daliserver
[2021-06-25 12:40:16] ERROR Can't find USB device
[2021-06-25 12:40:16] INFO Exiting

Wrong month shown in daliserver log

This is very odd! (But apparently harmless.)

The log entries from daliserver are being made with the wrong month. Year, day and time are all correct. An example from /var/log/daliserver.log today:

[2019-02-28 13:09:39] INFO Starting daliserver
[2019-02-28 13:09:39] INFO Kernel driver is active, trying to detach
[2019-02-28 13:09:39] INFO Server ready, waiting for events

daliserver was started with the command line /usr/bin/daliserver -l 172.30.54.65 -f /var/log/daliserver.log -u 2:7

Crash on OS X on premature connection close

Previously reported by @ben-knt on issue #11.

@ben-knt
By the way, I went through the Perl scripts and have some comments. Lampoff.pl crashes daliserver... for what reason, I don't know? I'm happy to help find out why. We can discuss offline sometime if you would like.

@onitake

Uh, what?
I know that usbdali.pm is a bit buggy and doesn't return the response correctly. I fixed that problem locally, but haven't pushed it to github yet.

Are you positive it crashes daliserver?

Ok, fixed usbdali.pm in bae41b1.

@ben-knt

I pulled in the changed file but no change. Lampoff.pl successfully turns off a lamp but the server crashes after. Two commands issued below:

Braque:perl benknuth$ perl allset.pl 255
Writing 4 bytes to socket, address=254 command=254
Received status:success response:0
Braque:perl benknuth$ perl lampoff.pl 12
Writing 4 bytes to socket, address=25 command=0
Braque:perl benknuth$

[2016-00-22 20:28:13] INFO Starting daliserver
[2016-00-22 20:28:13] INFO Server ready, waiting for events
[2016-00-22 20:28:32] INFO Got connection from 127.0.0.1:27089
[2016-00-22 20:28:32] INFO Got frame: 0x02 0x00 0xfe 0xfe
[2016-00-22 20:28:32] INFO Enqueued transfer (0x7fb7abc06cc0,0x7fb7abc06a90)
[2016-00-22 20:28:32] INFO Sending data to device:
0x00000000 12 01 00 03 00 00 fe fe 00 00 00 00 00 00 00 00 ................
0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Direction: USB<->DALI Sequence number: 01 Type: 16bit DALI Address: fe Command: fe
[2016-00-22 20:28:32] INFO Received data from device:
0x00000000 12 73 00 00 fe fe ff ff 01 00 00 00 00 00 00 00 .s..............
0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Direction: USB<->DALI Type: Send complete Address: fe Command: fe Status: ffff
[2016-00-22 20:28:32] INFO Received data from device:
0x00000000 12 71 00 00 00 00 00 8a 01 00 00 00 00 00 00 00 .q..............
0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Direction: USB<->DALI Type: Send response Response: ff Status: 008a Sequence number: 01
[2016-00-22 20:28:32] INFO Response to (0x00 0x00): 0xff [0x008a]
[2016-00-22 20:28:32] INFO Connection 9 was disconnected
[2016-00-22 20:28:32] INFO Closing connection 9
[2016-00-22 20:28:45] INFO Got connection from 127.0.0.1:27345
[2016-00-22 20:28:45] INFO Got frame: 0x02 0x00 0x19 0x00
[2016-00-22 20:28:45] INFO Enqueued transfer (0x7fb7abe01520,0x7fb7abe03090)
[2016-00-22 20:28:45] INFO Connection 9 was disconnected
[2016-00-22 20:28:45] INFO Closing connection 9
[2016-00-22 20:28:45] INFO Sending data to device:
0x00000000 12 02 00 03 00 00 19 00 00 00 00 00 00 00 00 00 ................
0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Direction: USB<->DALI Sequence number: 02 Type: 16bit DALI Address: 19 Command: 00
[2016-00-22 20:28:45] INFO Received data from device:
0x00000000 12 73 00 00 19 00 ff ff 02 00 00 00 00 00 00 00 .s..............
0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Direction: USB<->DALI Type: Send complete Address: 19 Command: 00 Status: ffff
[2016-00-22 20:28:45] INFO Received data from device:
0x00000000 12 71 00 00 00 00 00 8a 02 00 00 00 00 00 00 00 .q..............
0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Direction: USB<->DALI Type: Send response Response: ff Status: 008a Sequence number: 02
[2016-00-22 20:28:45] INFO Response to (0x00 0x00): 0xff [0x008a]
[2016-00-22 20:28:45] ERROR Error writing 4 bytes to connection 2059272945: Bad file descriptor
Segmentation fault: 11
Braque:~ benknuth$

Transactions

Some DALI command sequences are supposed to be sent as "transactions"; in particular, configuration commands that are sent twice must be sent as transactions. It looks like the DALI USB has some support for this (although not full support, surprisingly).

Example sequences:

    158 1493.945513    host        1.10.2      120320030000a50000 Special	A500	*	INITIALIZE (all) (sent twice)
    160 1493.963284    1.10.1      host        12730000a500ffff03 ... and we get
    162 1493.990289    1.10.1      host        12730000a500009103 ... two "1273" responses
    164 1494.000281    1.10.1      host        127100000000008a03 ... and a final "1271" response

If byte 2 is '0x20' then the command is transmitted twice. We get two '0x73' transmission reports, followed by the final 'no response' report.

      9 3.950105       host        1.10.2      125530030000ff2c55 DTR0=0x55 followed by SET SYSTEM FAILURE LEVEL (DTR0) (twice)
     11 3.967897       1.10.1      host        12730000a355ffff55 ... A355 (DTR0=0x55) transmission report 
     13 3.993899       1.10.1      host        12730000ff2c009155 ... FF2C (SET SYSTEM FAILURE LEVEL) transmission report
     15 4.020910       1.10.1      host        12730000ff2c009155 ... FF2C (SET SYSTEM FAILURE LEVEL) transmission report (again)
     17 4.030894       1.10.1      host        127100000000008a55 ... no response

If byte 2 is 0x30, byte 8 is used to construct a "Set DTR0" command, and then the command supplied in bytes 6–7 is sent twice. Transmission reports are received for all three forward frames, followed by the usual response status. (Annoyingly I was setting DTR0 to 0x55 here, which happened to be the same as the serial number. I have other examples where the serial number is different, however: there's one in issue #2.)

I didn't manage to get masterCONFIGURATOR to do any other transactions. A "QUERY NEXT DEVICE TYPE" sequence is supposed to be done as a transaction, but it doesn't:

    634 1497.094771    host        1.10.2      125200030000059900 Query	0599	A2	QUERY DEVICE TYPE
    636 1497.117224    1.10.1      host        127300000599009152 
    638 1497.132264    1.10.1      host        1272000000ff005d52   Answer	FF		= 255 (0xFF)
    640 1497.135044    host        1.10.2      12530003000005a700 Query	05A7	A2	QUERY NEXT DEVICE TYPE
    642 1497.158236    1.10.1      host        1273000005a7009153 
    644 1497.173239    1.10.1      host        127200000001005b53   Answer	01		= 1 (0x01)
    646 1497.177923    host        1.10.2      12540003000005a700 Query	05A7	A2	QUERY NEXT DEVICE TYPE
    648 1497.199249    1.10.1      host        1273000005a7009154 
    650 1497.214239    1.10.1      host        127200000006005d54   Answer	06		= 6 (0x06)
    652 1497.215556    host        1.10.2      12550003000005a700 Query	05A7	A2	QUERY NEXT DEVICE TYPE
    654 1497.240244    1.10.1      host        1273000005a7009155 
    656 1497.255239    1.10.1      host        1272000000fe005d55   Answer	FE		= 254 (0xFE)

Error when running daliserver

Hello there,
I am currently trying to use daliserver with a tridonic dali - usb adapter.
I am on macOS Sierra and I followed the step to install daliserver and the kext file is in my /System/Library/Extensions/

When I run the daliserver command (as root) I got an error:

$ daliserver
DALI USB multiplexer (daliserver)
Copyright (c) 2011 onitake All rights reserved.

[2018-05-06 15:08:06] INFO Starting daliserver
[2018-05-06 15:08:06] **ERROR** Can't find USB device
[2018-05-06 15:08:06] INFO Exiting

My tridonic is connected to my USB port but it is not connected to any DALI bus.

I am new with DALI so I still struggle to understand how everything works so I wonder if I need to connect a ballast to my adapter first or if I need to install any driver on my mac

In /dev/tty* I could not find any USB

Thank you for the help :)

DSI mode

This is not a request for implementation, it's just here in case anyone is interested! This is how DSI commands are sent:

      9 7.461141       host        1.10.2      12e00005000000ab00 DSI light level 171 (0xab)
     11 7.473145       1.10.1      host        117700000005ffff00 ... DSI mode
     13 7.491169       1.10.1      host        1275000000abffffe0 ... "1275" must be "DSI transmission report"?
     15 7.510169       1.10.1      host        1277000000060061e0 ... back to DALI mode
     17 12.044358      host        1.10.2      12e100050000feab00 DSI data for following command (0xfeab)
     19 12.056108      1.10.1      host        117700000005d4f400
     21 12.088108      1.10.1      host        12750000feabd4f4e1
     23 12.106086      1.10.1      host        1277000000060061e1
     25 12.118647      host        1.10.2      12e200050000ff4000 VERIFY CUSTOM CODE (0xff40)
     27 12.131085      1.10.1      host        117700000005011f00
     29 12.163087      1.10.1      host        12750000ff40011fe2
     31 12.181085      1.10.1      host        1277000000060061e2

configure.ac fails to detect libusb if pkg-config is not installed

When libusb-1.0 is installed through Homebrew on Mac OS X, it will not be detected by configure.ac, as this requires pkg-config. The Homebrew recipe does not depend on pkg-config. As a result, libusb-1.0.pc will not be installed.

Add a libusb detection method to configure.ac that does not depend on pkg-config.

Thanks to Paul Pritz for reporting this.

Receiving other DALI traffic

At the moment daliserver sends network messages when it receives information about 16-bit forward frames observed on the bus from the DALI USB device (packets starting 0x11 0x73), but doesn't send any information about backward frames.

The DALI USB device does also report backward frames it observes, in packets starting 0x11 0x72:

      9 227.191712     1.10.1      host        117300000000ffff00 Received DAPC(0) for A0
     11 273.176066     1.10.1      host        117300000080ffff00 Received DAPC(0x80) for A0
     13 327.736250     1.10.1      host        117300000280ffff00 Received DAPC(0x80) for A1
     15 329.848232     1.10.1      host        117300000280622c00 Received DAPC(0x80) for A1
     17 332.344191     1.10.1      host        117300000200744a00 Received DAPC(0) for A1
     19 439.993644     1.10.1      host        1173000003a0ffff00 Received QUERY ACTUAL LEVEL for A1
     21 440.005665     1.10.1      host        117200000000003e00 Received reply 0
     39 527.888602     host        1.10.2      120100030000ff0600 Send broadcast RECALL MIN LEVEL
     41 527.906402     1.10.1      host        12730000ff06ffff01 ... transmission report
     43 527.917386     1.10.1      host        127100000000008a01 ... no response
     45 561.337931     1.10.1      host        1173000003a0ffff00 Received QUERY ACTUAL LEVEL for A1
     47 561.350888     1.10.1      host        117200000001003d00 Received reply 0x01
     49 613.562179     1.10.1      host        1173000003a0ffff00 Received QUERY ACTUAL LEVEL for A1
     51 613.575177     1.10.1      host        117200000001003e00 Received reply 0x01
     53 640.250805     1.10.1      host        1173000005a0ffff00 Received QUERY ACTUAL LEVEL for A2
     55 640.265802     1.10.1      host        117200000055005c00 Received reply 0x55
     57 657.082566     1.10.1      host        1173000005a0ffff00 Received QUERY ACTUAL LEVEL for A2
     59 657.097562     1.10.1      host        117200000055005b00 Received reply 0x55
     69 1118.068048    host        1.10.2      120200030000ff0000 Send broadcast OFF
     71 1118.085960    1.10.1      host        12730000ff00ffff02 ... transmission report
     73 1118.096959    1.10.1      host        127100000000008a02 ... no response
     75 1118.393445    host        1.10.2      120300030000ff0000 Send broadcast OFF
     77 1118.411935    1.10.1      host        12730000ff000e9703 ... transmission report
     79 1118.422954    1.10.1      host        127100000000008a03 ... no response
    105 3158.917796    1.10.1      host        117700000005ffff00 DSI mode (briefly shorting the bus)
    107 3159.023810    1.10.1      host        117700000006006100 DALI mode
    109 3159.553784    1.10.1      host        11770000000518b900 DSI mode
    111 3159.683781    1.10.1      host        117700000006006100 DALI mode
    113 3182.744513    1.10.1      host        117700000005ffff00 DSI mode
    115 3182.836474    1.10.1      host        117700000006006100 DALI mode
    117 3182.915486    1.10.1      host        11770000000503a900 DSI mode
    119 3183.028523    1.10.1      host        117700000006006100 DALI mode

Notice what's happening in bytes 6–7 of the responses. Most of the forward frames have 0xffff here, but some of them don't (eg. 0x622c in packet 15). Most of the response frames have low numbers here; in particular, "no response" always has 0x008a, and "response" always has a number below 0x008a (eg. 0x003d or 0x003e for A1, 0x005c or 0x005b for A2).

I believe this field represents the length of time since the previous bus activity, in units of 100µs; 0x8a translates to 13.8ms. Table 20 of IEC62386-101 states that the minimum settling time between forward frame and backward frame for a frame not to be interpreted as a backward frame is 13.4ms. I'm not sure exactly when it starts counting; it could be allowing a half-bit time (400µs) with no transition to recognise that the transmitter has stopped.

Let's check by having a look at the transmission reports during a transaction (see issue #22):

      9 3.950105       host        1.10.2      125530030000ff2c55 DTR0=0x55 followed by SET SYSTEM FAILURE LEVEL (DTR0) (twice)
     11 3.967897       1.10.1      host        12730000a355ffff55 ... A355 (DTR0=0x55) transmission report 
     13 3.993899       1.10.1      host        12730000ff2c009155 ... FF2C (SET SYSTEM FAILURE LEVEL) transmission report
     15 4.020910       1.10.1      host        12730000ff2c009155 ... FF2C (SET SYSTEM FAILURE LEVEL) transmission report (again)
     17 4.030894       1.10.1      host        127100000000008a55 ... no response

0x0091 is 14.5ms. Table 17 of IEC62386-101 gives the minimum settling time between send-twice forward frames as 13.5ms and the maximum as 75.0ms, so 0x0091 is comfortably within this range.

What about the times observed for backward responses from the two DALI devices I've been experimenting with? A1 in the above examples is a LTECH LED driver, A2 is a Tridonic EM powerLED PRO DIM C. 0x003d is 6.1ms, 0x5b is 9.1ms. Table 17 gives the minimum settling time between a forward frame and the start of transmission of a backward frame as 5.5ms and the maximum time as 10.5, so both times are within the range.

I don't know what the DALI USB does when it observes an 8-bit frame that does not start within 13.4ms of the end of a forward frame; I don't have any equipment that will generate one to test! It might do nothing, or it might send a type 0x72 packet. To be on the safe side, we should probably check that the time field is less than 0x8a for all packets of type 0x72.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.