Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sknull committed Apr 11, 2023
0 parents commit 9feabe9
Show file tree
Hide file tree
Showing 121 changed files with 114,985 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.idea/
**/target/
lsp/
*.log
*.iml
45 changes: 45 additions & 0 deletions Readme.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
= Klanglicht 3 - Kotlin
:doctype: book
:description: Documentation for the klanglicht project.
:keywords: kotlin, dmx, serial-usb
:icons: font
:toc:
:toc-title: Inhalt
:toclevels: 10

== History and current state of the project (April 2023)

=== History

This project started as a simple proof of concept if it would be possible to get some simple RGB light working
using a DMX interface.

After researching about possible interfaces I decided for the Eurolite DMX Pro 512 for which I was able to find some information about it's link:/resources/hardware/Eurolite_USB-DMX512-PRO_serial-protocol_discussion-thread.pdf[serial protocol].

Another challenge was to find a reliable serial port library which is also available on other platforms as Windows which I use to develop the software. Here I decided for the fazecast jSerialComm library which also is playing well on the raspberry pi mini computers (which was mandatory as I wanted to set up a little raspy applicance lighting in the living room).

After playing around a bit a first prototype was implemented which was able to fade in a color from black and vice versa (wow!).

In the meantime (and about four years later) the project went through different stages ranging from kind of modular approach to pure chaos messing up UI code with logic.
In between the UI was also capable of reading and playing time code. But this turned out
not to be reliable enough to use it in production environments so I tossed it.

The current flavor of the project which you have in front of you now has a layered approach which strictly separates the technical stuff needed to talk with the DMX interface from the client code. The interface to the outside world such as a JAva-FX UI which still exists and a web interface which is not fully implemented yet is the REST module.
With this approach it is possible to run the server component in form a little shaded jar on a mini computer such as the raspberry pi while running a complex user interface elsewhere.

This is a complete rewrite in kotlin as I no longer want to write java code.
To make my life easier a bit and because I only have access to one DMX interface which I anaged to find documentation for, I have
boiled down everything to be dedicated to this which is the Eurolite DMX 512 Pro.

A printout of the thread with those golden informations which kick started me once back and the original hardware manual are attached to this project

* link:docs/hardware/eurolite/Eurolite_USB-DMX512-PRO_serial-protocol_discussion-thread.pdf[Discussion Thread (German)]
* link:docs/hardware/eurolite/Eurolite_USB-DMX512_PRO_manual_german.pdf[Manual (German)]

== Current State

As I just started to convert the project to kotlin (the original Java code will remain private as ther is a lot of chaos...)
the REST interface itself is not yet implemented (also because I do not know which framework I want to use.
Spring boot seems not to be appropriate anymore along with kotlin and Micronaut seems to be abandonded and my
current experience with the latter during my professional work life is not so good.

Binary file not shown.
Binary file not shown.
76 changes: 76 additions & 0 deletions klanglicht-core/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>

<parent>
<groupId>de.visualdigits.kotlin</groupId>
<artifactId>klanglicht-kt</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>klanglicht-core</artifactId>
<version>${revision}</version>

<dependencies>

<!-- common stuff -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>

<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${version.jackson}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${version.jackson}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${version.jackson}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>${version.jackson}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>${version.jackson}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${version.jackson}</version>
</dependency>

<!-- serial port -->
<dependency>
<groupId>io.github.java-native</groupId>
<artifactId>jssc</artifactId>
<version>2.9.4</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package de.visualdigits.kotlin.klanglicht.dmx

import org.apache.commons.lang3.StringUtils


abstract class AbstractDMXInterface : DMXInterface {

protected val dmxFrame: DmxFrame = DmxFrame()

override fun toString(): String {
return dmxFrame.dump()
}

override fun repr(): String {
val lst = ArrayList<String>()
for (b in dmxFrame.frame()) {
lst.add(StringUtils.leftPad(Integer.toHexString(b.toInt()), 8, '0'))
}
return lst.toString()
}

override fun open(portName: String) {}

override fun close() {}

override fun read(): ByteArray {
return dmxFrame.data
}

override fun write(dmxFrame: DmxFrame) {}

override fun setChannel(channel: Int, value: Int) {
dmxFrame.set(channel, value)
}

override fun setData(baseChannel: Int, data: ByteArray) {
dmxFrame.set(baseChannel, data)
}

override fun clearChannels() {
for (i in 1..511) {
setChannel(i, 0)
}
}

override fun getChannel(channel: Int): Int {
return dmxFrame.get(4 + channel)
}

override fun clear() {
initFrame()
write()
}

override fun initFrame() {
dmxFrame.init()
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package de.visualdigits.kotlin.klanglicht.dmx


interface DMXInterface {

/**
* Opens the given serial port for communication with the physical device.
*
* @param portName The port name (i.e. 'COM9'.
*/
fun open(portName: String)

/**
* Closes the current serial port.
*/
fun close()

/**
* Writes the current internal data bytes to the device.
*/
fun write()

/**
* Writes the given data bytes to the device.
*
* @param data The frame data to write.
*/
fun write(data: ByteArray)

/**
* Writes the given data bytes to the device.
*
* @param dmxFrame The frame data to write.
*/
fun write(dmxFrame: DmxFrame)

/**
* Returns the current frame data.
*/
fun read(): ByteArray?

/**
* Clears all 512 DMX channels in the internal data bytes by setting to 0.
*/
fun clearChannels()

/**
* Sets the given DMX channel in the internal data bytes to the given value.
*
* @param channel The channel to set [1..512].
* @param value The value to set [0..255].
*/
fun setChannel(channel: Int, value: Int)

/**
* Sets the given DMX channel in the internal data bytes to the given value.
*
* @param baseChannel The base channel to set [1..512].
* @param data The bytes to set.
*/
fun setData(baseChannel: Int, data: ByteArray)

/**
* Returns the currently set value of the given DMX channel.<br></br>
* This method does not actually read from the device but returns
* the current value form the internal data bytes.
*
* @param channel The DMX channel to retrieve [1..512].
* @return byte
*/
fun getChannel(channel: Int): Int

/**
* Initializes a new DMX frame appropriate for the target device.
*/
fun initFrame()

/**
* Sets all channels to 0.
*/
fun clear()

fun repr(): String?

fun isOpen(): Boolean

companion object {

private var dmxInterface: DMXInterface? = null

fun instance(): DMXInterface = dmxInterface!!

fun load(type: DMXInterfaceType): DMXInterface {
if (dmxInterface == null) {
dmxInterface = when (type) {
DMXInterfaceType.Eurolite512Pro -> DMXInterfaceEurolite512Pro()
DMXInterfaceType.Dummy -> DMXInterfaceDummy()
DMXInterfaceType.Rest -> DMXInterfaceRest()
}
}
return dmxInterface!!
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package de.visualdigits.kotlin.klanglicht.dmx


class DMXInterfaceDummy() : AbstractDMXInterface() {

override fun toString(): String {
return repr()
}

override fun open(portName: String) {
println("### open")
}

override fun close() {
println("### close")
}

override fun write() {
println(toString())
}

override fun read(): ByteArray {
return ByteArray(0)
}

override fun write(data: ByteArray) {}

override fun isOpen(): Boolean = true
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package de.visualdigits.kotlin.klanglicht.dmx

import jssc.SerialPort
import jssc.SerialPortException
import org.apache.commons.lang3.StringUtils

class DMXInterfaceEurolite512Pro : AbstractDMXInterface() {

private var serialPort: SerialPort? = null

override fun open(portName: String) {
serialPort = null
if (!StringUtils.isEmpty(portName)) {
try {
serialPort = SerialPort(portName)
serialPort?.openPort()
serialPort?.setParams(9600, 8, 1, 0)
} catch (e: Exception) {
System.err.println("Could not open DMX port '$portName'")
}
}
}

override fun close() {
if (isOpen()) {
try {
serialPort?.closePort()
} catch (e: SerialPortException) {
throw IllegalStateException("Could not close port", e)
}
}
}

override fun write() {
write(dmxFrame)
}

override fun write(data: ByteArray) {
write(DmxFrame(data))
}

override fun write(dmxFrame: DmxFrame) {
if (isOpen()) {
try {
val frame = dmxFrame.frame()
serialPort?.writeBytes(frame)
} catch (e: SerialPortException) {
throw IllegalStateException("Could write dmxFrame to port", e)
}
} else {
throw IllegalStateException("Tried to write to non open port")
}
}

override fun isOpen(): Boolean {
return serialPort != null && serialPort?.isOpened == true
}
}
Loading

0 comments on commit 9feabe9

Please sign in to comment.