mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Merge branch 'add_effect_engine'
Conflicts: config/hyperion.config.json libsrc/bootsequence/KittBootSequence.h Former-commit-id: ca9492accdf2ffdad52f6c0695cbba955f802b97
This commit is contained in:
commit
d56027da20
@ -1,6 +1,6 @@
|
|||||||
ON RASPBERRY
|
ON RASPBERRY
|
||||||
--------------
|
--------------
|
||||||
sudo apt-get install libprotobuf-dev libQt4-dev libusb-1.0-0-dev libudev-dev rsync
|
sudo apt-get install libprotobuf-dev libQt4-dev libusb-1.0-0-dev python-dev rsync
|
||||||
|
|
||||||
ON HOST
|
ON HOST
|
||||||
---------
|
---------
|
||||||
|
@ -10,7 +10,18 @@ repodir="$2"
|
|||||||
echo build directory = $builddir
|
echo build directory = $builddir
|
||||||
echo repository root dirrectory = $repodir
|
echo repository root dirrectory = $repodir
|
||||||
|
|
||||||
echo Copying binaries
|
outfile="$repodir/deploy/hyperion.tar.gz"
|
||||||
cp -v "$builddir"/bin/hyperiond "$repodir"/deploy
|
echo create $outfile
|
||||||
cp -v "$builddir"/bin/hyperion-remote "$repodir"/deploy
|
|
||||||
cp -v "$builddir"/bin/gpio2spi "$repodir"/deploy
|
tar --create --verbose --gzip --absolute-names --show-transformed-names \
|
||||||
|
--file "$outfile" \
|
||||||
|
--transform "s:$builddir/bin/:hyperion/bin/:" \
|
||||||
|
--transform "s:$repodir/effects/:hyperion/effects/:" \
|
||||||
|
--transform "s:$repodir/config/:hyperion/config/:" \
|
||||||
|
--transform "s://:/:g" \
|
||||||
|
"$builddir/bin/hyperiond" \
|
||||||
|
"$builddir/bin/hyperion-remote" \
|
||||||
|
"$builddir/bin/gpio2spi" \
|
||||||
|
"$builddir/bin/dispmanx2png" \
|
||||||
|
"$repodir/effects/"* \
|
||||||
|
"$repodir/config/hyperion.config.json"
|
||||||
|
@ -16,28 +16,26 @@ fi
|
|||||||
# Stop hyperion daemon if it is running
|
# Stop hyperion daemon if it is running
|
||||||
/sbin/initctl stop hyperion
|
/sbin/initctl stop hyperion
|
||||||
|
|
||||||
# Get the Hyperion executable
|
# Get and extract the Hyperion binaries and effects to /opt
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperiond -P /usr/bin/
|
wget https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz -O - | tar -C /opt -xz
|
||||||
chmod +x /usr/bin/hyperiond
|
|
||||||
|
|
||||||
# Get the Hyperion command line utility
|
# create links to the binaries
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion-remote -P /usr/bin/
|
ln -fs /opt/hyperion/bin/hyperiond /usr/bin/hyperiond
|
||||||
chmod +x /usr/bin/hyperion-remote
|
ln -fs /opt/hyperion/bin/hyperion-remote /usr/bin/hyperion-remote
|
||||||
|
|
||||||
# Copy the gpio changer (gpio->spi) to the /usr/bin
|
# create link to the gpio changer (gpio->spi)
|
||||||
if [ $IS_XBIAN -eq 0 ]; then
|
if [ $IS_XBIAN -eq 0 ]; then
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/deploy/gpio2spi -P /usr/bin/
|
ln -fs /opt/hyperion/bin/gpio2spi /usr/bin/gpio2spi
|
||||||
chmod +x /usr/bin/gpio2spi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy the hyperion configuration file to /etc
|
# Copy a link to the hyperion configuration file to /etc
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/config/hyperion.config.json -P /etc/
|
ln -s /opt/hyperion/config/hyperion.config.json /etc/hyperion.config.json
|
||||||
|
|
||||||
# Copy the service control configuration to /etc/int
|
# Copy the service control configuration to /etc/int
|
||||||
if [ $IS_XBIAN -eq 0 ]; then
|
if [ $IS_XBIAN -eq 0 ]; then
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.conf -P /etc/init/
|
wget -N https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.conf -P /etc/init/
|
||||||
else
|
else
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.xbian.conf -O /etc/init/hyperion.conf
|
wget -N https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.xbian.conf -O /etc/init/hyperion.conf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Start the hyperion daemon
|
# Start the hyperion daemon
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Script for downloading and installing the latest Hyperion release
|
|
||||||
|
|
||||||
# Find out if we are on XBian
|
|
||||||
IS_XBIAN=`cat /etc/issue | grep XBian | wc -l`
|
|
||||||
|
|
||||||
# Make sure that the boblight daemon is no longer running
|
|
||||||
BOBLIGHT_PROCNR=$(ps -e | grep "boblight" | wc -l)
|
|
||||||
if [ $BOBLIGHT_PROCNR -eq 1 ];
|
|
||||||
then
|
|
||||||
echo 'Found running instance of boblight. Please stop boblight via XBMC menu before installing hyperion'
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Stop hyperion daemon if it is running
|
|
||||||
/sbin/initctl stop hyperion
|
|
||||||
|
|
||||||
# Get the Hyperion executable
|
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperiond -P /usr/bin/
|
|
||||||
chmod +x /usr/bin/hyperiond
|
|
||||||
|
|
||||||
# Get the Hyperion command line utility
|
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion-remote -P /usr/bin/
|
|
||||||
chmod +x /usr/bin/hyperion-remote
|
|
||||||
|
|
||||||
# Copy the gpio changer (gpio->spi) to the /usr/bin
|
|
||||||
if [ $IS_XBIAN -eq 0 ]; then
|
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/deploy/gpio2spi -P /usr/bin/
|
|
||||||
chmod +x /usr/bin/gpio2spi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy the service control configuration to /etc/int
|
|
||||||
if [ $IS_XBIAN -eq 0 ]; then
|
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.conf -P /etc/init/
|
|
||||||
else
|
|
||||||
wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.xbian.conf -P /etc/init/ -O hyperion.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start the hyperion daemon
|
|
||||||
/sbin/initctl start hyperion
|
|
@ -15,51 +15,69 @@
|
|||||||
"name" : "MyPi",
|
"name" : "MyPi",
|
||||||
"type" : "ws2801",
|
"type" : "ws2801",
|
||||||
"output" : "/dev/spidev0.0",
|
"output" : "/dev/spidev0.0",
|
||||||
"rate" : 500000,
|
"rate" : 250000,
|
||||||
"colorOrder" : "rgb"
|
"colorOrder" : "rgb"
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Color manipulation configuration used to tune the output colors to specific surroundings. Contains the following fields:
|
/// Color manipulation configuration used to tune the output colors to specific surroundings.
|
||||||
/// * 'hsv' : The manipulation in the Hue-Saturation-Value color domain with the following tuning parameters:
|
/// The configuration contains a list of color-transforms. Each transform contains the
|
||||||
|
/// following fields:
|
||||||
|
/// * 'id' : The unique identifier of the color transformation (eg 'device_1')
|
||||||
|
/// * 'leds' : The indices (or index ranges) of the leds to which this color transform applies
|
||||||
|
/// (eg '0-5, 9, 11, 12-17'). The indices are zero based.
|
||||||
|
/// * 'hsv' : The manipulation in the Hue-Saturation-Value color domain with the following
|
||||||
|
/// tuning parameters:
|
||||||
/// - 'saturationGain' The gain adjustement of the saturation
|
/// - 'saturationGain' The gain adjustement of the saturation
|
||||||
/// - 'valueGain' The gain adjustement of the value
|
/// - 'valueGain' The gain adjustement of the value
|
||||||
/// * 'red'/'green'/'blue' : The manipulation in the Red-Green-Blue color domain with the following tuning parameters for each channel:
|
/// * 'red'/'green'/'blue' : The manipulation in the Red-Green-Blue color domain with the
|
||||||
/// - 'threshold' The minimum required input value for the channel to be on (else zero)
|
/// following tuning parameters for each channel:
|
||||||
|
/// - 'threshold' The minimum required input value for the channel to be on
|
||||||
|
/// (else zero)
|
||||||
/// - 'gamma' The gamma-curve correction factor
|
/// - 'gamma' The gamma-curve correction factor
|
||||||
/// - 'blacklevel' The lowest possible value (when the channel is black)
|
/// - 'blacklevel' The lowest possible value (when the channel is black)
|
||||||
/// - 'whitelevel' The highest possible value (when the channel is white)
|
/// - 'whitelevel' The highest possible value (when the channel is white)
|
||||||
/// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning parameters:
|
///
|
||||||
|
/// Next to the list with color transforms there is also a smoothing option.
|
||||||
|
/// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning
|
||||||
|
/// parameters:
|
||||||
/// - 'type' The type of smoothing algorithm ('linear' or 'none')
|
/// - 'type' The type of smoothing algorithm ('linear' or 'none')
|
||||||
/// - 'time_ms' The time constant for smoothing algorithm in milliseconds
|
/// - 'time_ms' The time constant for smoothing algorithm in milliseconds
|
||||||
/// - 'updateFrequency' The update frequency of the leds in Hz
|
/// - 'updateFrequency' The update frequency of the leds in Hz
|
||||||
"color" :
|
"color" :
|
||||||
{
|
{
|
||||||
|
"transform" :
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id" : "default",
|
||||||
|
"leds" : "*",
|
||||||
"hsv" :
|
"hsv" :
|
||||||
{
|
{
|
||||||
"saturationGain" : 1.0000,
|
"saturationGain" : 1.0000,
|
||||||
"valueGain" : 1.5000
|
"valueGain" : 1.0000
|
||||||
},
|
},
|
||||||
"red" :
|
"red" :
|
||||||
{
|
{
|
||||||
"threshold" : 0.1000,
|
"threshold" : 0.0000,
|
||||||
"gamma" : 2.0000,
|
"gamma" : 1.0000,
|
||||||
"blacklevel" : 0.0000,
|
"blacklevel" : 0.0000,
|
||||||
"whitelevel" : 0.8000
|
"whitelevel" : 1.0000
|
||||||
},
|
},
|
||||||
"green" :
|
"green" :
|
||||||
{
|
{
|
||||||
"threshold" : 0.1000,
|
"threshold" : 0.0000,
|
||||||
"gamma" : 2.0000,
|
"gamma" : 1.0000,
|
||||||
"blacklevel" : 0.0000,
|
"blacklevel" : 0.0000,
|
||||||
"whitelevel" : 1.0000
|
"whitelevel" : 1.0000
|
||||||
},
|
},
|
||||||
"blue" :
|
"blue" :
|
||||||
{
|
{
|
||||||
"threshold" : 0.1000,
|
"threshold" : 0.0000,
|
||||||
"gamma" : 2.0000,
|
"gamma" : 1.0000,
|
||||||
"blacklevel" : 0.0000,
|
"blacklevel" : 0.0000,
|
||||||
"whitelevel" : 1.0000
|
"whitelevel" : 1.0000
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"smoothing" :
|
"smoothing" :
|
||||||
{
|
{
|
||||||
"type" : "none",
|
"type" : "none",
|
||||||
@ -338,12 +356,19 @@
|
|||||||
"enable" : true
|
"enable" : true
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The boot-sequence configuration, contains the following items:
|
/// The configuration of the effect engine, contains the following items:
|
||||||
/// * type : The type of the boot-sequence ('rainbow', 'knightrider', 'none')
|
/// * paths : An array with absolute location(s) of directories with effects
|
||||||
/// * duration_ms : The length of the boot-sequence [ms]
|
"effects" :
|
||||||
|
{
|
||||||
|
"paths" :
|
||||||
|
[
|
||||||
|
"/opt/hyperion/effects"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
"bootsequence" :
|
"bootsequence" :
|
||||||
{
|
{
|
||||||
"type" : "Rainbow",
|
"effect" : "Rainbow swirl fast",
|
||||||
"duration_ms" : 3000
|
"duration_ms" : 3000
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -358,7 +383,8 @@
|
|||||||
"frequency_Hz" : 10.0
|
"frequency_Hz" : 10.0
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields:
|
/// The configuration of the XBMC connection used to enable and disable the frame-grabber.
|
||||||
|
/// Contains the following fields:
|
||||||
/// * xbmcAddress : The IP address of the XBMC-host
|
/// * xbmcAddress : The IP address of the XBMC-host
|
||||||
/// * xbmcTcpPort : The TCP-port of the XBMC-server
|
/// * xbmcTcpPort : The TCP-port of the XBMC-server
|
||||||
/// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback
|
/// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback
|
||||||
@ -396,5 +422,5 @@
|
|||||||
// "port" : 19333
|
// "port" : 19333
|
||||||
// },
|
// },
|
||||||
|
|
||||||
"end-of-json" : "end-of-json"
|
"endOfJson" : "endOfJson"
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,38 @@
|
|||||||
"transform" :
|
"transform" :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id" : "MyPi device",
|
"id" : "MyPi red",
|
||||||
"leds" : "0-49",
|
"leds" : "0-11,37-49",
|
||||||
|
"hsv" :
|
||||||
|
{
|
||||||
|
"saturationGain" : 1.0000,
|
||||||
|
"valueGain" : 1.5000
|
||||||
|
},
|
||||||
|
"red" :
|
||||||
|
{
|
||||||
|
"threshold" : 0.1000,
|
||||||
|
"gamma" : 2.0000,
|
||||||
|
"blacklevel" : 0.3000,
|
||||||
|
"whitelevel" : 0.8000
|
||||||
|
},
|
||||||
|
"green" :
|
||||||
|
{
|
||||||
|
"threshold" : 0.1000,
|
||||||
|
"gamma" : 2.0000,
|
||||||
|
"blacklevel" : 0.0000,
|
||||||
|
"whitelevel" : 1.0000
|
||||||
|
},
|
||||||
|
"blue" :
|
||||||
|
{
|
||||||
|
"threshold" : 0.1000,
|
||||||
|
"gamma" : 2.0000,
|
||||||
|
"blacklevel" : 0.0000,
|
||||||
|
"whitelevel" : 1.0000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : "MyPi blue",
|
||||||
|
"leds" : "12-36",
|
||||||
"hsv" :
|
"hsv" :
|
||||||
{
|
{
|
||||||
"saturationGain" : 1.0000,
|
"saturationGain" : 1.0000,
|
||||||
@ -63,7 +93,7 @@
|
|||||||
"threshold" : 0.1000,
|
"threshold" : 0.1000,
|
||||||
"gamma" : 2.0000,
|
"gamma" : 2.0000,
|
||||||
"blacklevel" : 0.0000,
|
"blacklevel" : 0.0000,
|
||||||
"whitelevel" : 1.0000
|
"whitelevel" : 0.3000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -345,6 +375,40 @@
|
|||||||
"enable" : true
|
"enable" : true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"effects" :
|
||||||
|
{
|
||||||
|
"Rainbow swirl" :
|
||||||
|
{
|
||||||
|
"script" : "/home/pi/hyperion/effects/rainbow-swirl.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"rotation-time" : 10.0,
|
||||||
|
"brightness" : 1.0,
|
||||||
|
"reverse" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Rainbow mood" :
|
||||||
|
{
|
||||||
|
"script" : "/home/pi/hyperion/effects/rainbow-mood.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"rotation-time" : 10.0,
|
||||||
|
"brightness" : 1.0,
|
||||||
|
"reverse" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Knight rider" :
|
||||||
|
{
|
||||||
|
"script" : "/home/pi/hyperion/effects/knight-rider.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"speed" : 1.0,
|
||||||
|
"fadeFactor" : 0.7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/// The boot-sequence configuration, contains the following items:
|
/// The boot-sequence configuration, contains the following items:
|
||||||
/// * type : The type of the boot-sequence ('rainbow', 'knightrider', 'none')
|
/// * type : The type of the boot-sequence ('rainbow', 'knightrider', 'none')
|
||||||
/// The boot-sequence configuration, contains the following items:
|
/// The boot-sequence configuration, contains the following items:
|
||||||
@ -352,7 +416,13 @@
|
|||||||
/// * duration_ms : The length of the boot-sequence [ms]
|
/// * duration_ms : The length of the boot-sequence [ms]
|
||||||
"bootsequence" :
|
"bootsequence" :
|
||||||
{
|
{
|
||||||
"type" : "Rainbow",
|
"script" : "/home/pi/hyperion/effects/rainbow-swirl.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"rotation-time" : 3.0,
|
||||||
|
"brightness" : 1.0,
|
||||||
|
"reverse" : false
|
||||||
|
},
|
||||||
"duration_ms" : 3000
|
"duration_ms" : 3000
|
||||||
},
|
},
|
||||||
|
|
@ -1 +1 @@
|
|||||||
a63791e2794fa0c76a2a5da3d0ce7b3e4f4089ff
|
0e33a5994203b58676a56efa2addf362a276c219
|
Binary file not shown.
BIN
deploy/gpio2spi
BIN
deploy/gpio2spi
Binary file not shown.
Binary file not shown.
BIN
deploy/hyperion.tar.gz
Normal file
BIN
deploy/hyperion.tar.gz
Normal file
Binary file not shown.
@ -1 +0,0 @@
|
|||||||
cb10b0acda414c8d57cd1cc1bf49c48c142ec6b8
|
|
9
effects/knight-rider.json
Executable file
9
effects/knight-rider.json
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name" : "Knight rider",
|
||||||
|
"script" : "knight-rider.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"speed" : 1.0,
|
||||||
|
"fadeFactor" : 0.7
|
||||||
|
}
|
||||||
|
}
|
49
effects/knight-rider.py
Normal file
49
effects/knight-rider.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import hyperion
|
||||||
|
import time
|
||||||
|
import colorsys
|
||||||
|
|
||||||
|
# Get the rotation time
|
||||||
|
speed = float(hyperion.args.get('speed', 1.0))
|
||||||
|
fadeFactor = float(hyperion.args.get('fadeFactor', 0.7))
|
||||||
|
|
||||||
|
# Check parameters
|
||||||
|
speed = max(0.0001, speed)
|
||||||
|
fadeFactor = max(0.0, min(fadeFactor, 1.0))
|
||||||
|
|
||||||
|
# Initialize the led data
|
||||||
|
width = 25
|
||||||
|
imageData = bytearray(width * (0,0,0))
|
||||||
|
imageData[0] = 255
|
||||||
|
|
||||||
|
# Calculate the sleep time and rotation increment
|
||||||
|
increment = 1
|
||||||
|
sleepTime = 1.0 / (speed * width)
|
||||||
|
while sleepTime < 0.05:
|
||||||
|
increment *= 2
|
||||||
|
sleepTime *= 2
|
||||||
|
|
||||||
|
# Start the write data loop
|
||||||
|
position = 0
|
||||||
|
direction = 1
|
||||||
|
while not hyperion.abort():
|
||||||
|
hyperion.setImage(width, 1, imageData)
|
||||||
|
|
||||||
|
# Move data into next state
|
||||||
|
for i in range(increment):
|
||||||
|
position += direction
|
||||||
|
if position == -1:
|
||||||
|
position = 1
|
||||||
|
direction = 1
|
||||||
|
elif position == width:
|
||||||
|
position = width-2
|
||||||
|
direction = -1
|
||||||
|
|
||||||
|
# Fade the old data
|
||||||
|
for j in range(width):
|
||||||
|
imageData[3*j] = int(fadeFactor * imageData[3*j])
|
||||||
|
|
||||||
|
# Insert new data
|
||||||
|
imageData[3*position] = 255
|
||||||
|
|
||||||
|
# Sleep for a while
|
||||||
|
time.sleep(sleepTime)
|
12
effects/mood-blobs-blue.json
Executable file
12
effects/mood-blobs-blue.json
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name" : "Blue mood blobs",
|
||||||
|
"script" : "mood-blobs.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"rotationTime" : 60.0,
|
||||||
|
"color" : [0,0,255],
|
||||||
|
"hueChange" : 60.0,
|
||||||
|
"blobs" : 5,
|
||||||
|
"reverse" : false
|
||||||
|
}
|
||||||
|
}
|
12
effects/mood-blobs-green.json
Executable file
12
effects/mood-blobs-green.json
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name" : "Green mood blobs",
|
||||||
|
"script" : "mood-blobs.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"rotationTime" : 60.0,
|
||||||
|
"color" : [0,255,0],
|
||||||
|
"hueChange" : 60.0,
|
||||||
|
"blobs" : 5,
|
||||||
|
"reverse" : false
|
||||||
|
}
|
||||||
|
}
|
12
effects/mood-blobs-red.json
Executable file
12
effects/mood-blobs-red.json
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name" : "Red mood blobs",
|
||||||
|
"script" : "mood-blobs.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"rotationTime" : 60.0,
|
||||||
|
"color" : [255,0,0],
|
||||||
|
"hueChange" : 60.0,
|
||||||
|
"blobs" : 5,
|
||||||
|
"reverse" : false
|
||||||
|
}
|
||||||
|
}
|
61
effects/mood-blobs.py
Normal file
61
effects/mood-blobs.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import hyperion
|
||||||
|
import time
|
||||||
|
import colorsys
|
||||||
|
import math
|
||||||
|
|
||||||
|
# Get the parameters
|
||||||
|
rotationTime = float(hyperion.args.get('rotationTime', 20.0))
|
||||||
|
color = hyperion.args.get('color', (0,0,255))
|
||||||
|
hueChange = float(hyperion.args.get('hueChange', 60.0)) / 360.0
|
||||||
|
blobs = int(hyperion.args.get('blobs', 5))
|
||||||
|
reverse = bool(hyperion.args.get('reverse', False))
|
||||||
|
|
||||||
|
# Check parameters
|
||||||
|
rotationTime = max(0.1, rotationTime)
|
||||||
|
hueChange = max(0.0, min(abs(hueChange), .5))
|
||||||
|
blobs = max(1, blobs)
|
||||||
|
|
||||||
|
# Calculate the color data
|
||||||
|
baseHsv = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
|
||||||
|
colorData = bytearray()
|
||||||
|
for i in range(hyperion.ledCount):
|
||||||
|
hue = (baseHsv[0] + hueChange * math.sin(2*math.pi * i / hyperion.ledCount)) % 1.0
|
||||||
|
rgb = colorsys.hsv_to_rgb(hue, baseHsv[1], baseHsv[2])
|
||||||
|
colorData += bytearray((int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2])))
|
||||||
|
|
||||||
|
# Calculate the increments
|
||||||
|
sleepTime = 0.1
|
||||||
|
amplitudePhaseIncrement = blobs * math.pi * sleepTime / rotationTime
|
||||||
|
colorDataIncrement = 3
|
||||||
|
|
||||||
|
# Switch direction if needed
|
||||||
|
if reverse:
|
||||||
|
amplitudePhaseIncrement = -amplitudePhaseIncrement
|
||||||
|
colorDataIncrement = -colorDataIncrement
|
||||||
|
|
||||||
|
# create a Array for the colors
|
||||||
|
colors = bytearray(hyperion.ledCount * (0,0,0))
|
||||||
|
|
||||||
|
# Start the write data loop
|
||||||
|
amplitudePhase = 0.0
|
||||||
|
rotateColors = False
|
||||||
|
while not hyperion.abort():
|
||||||
|
# Calculate new colors
|
||||||
|
for i in range(hyperion.ledCount):
|
||||||
|
amplitude = max(0.0, math.sin(-amplitudePhase + 2*math.pi * blobs * i / hyperion.ledCount))
|
||||||
|
colors[3*i+0] = int(colorData[3*i+0] * amplitude)
|
||||||
|
colors[3*i+1] = int(colorData[3*i+1] * amplitude)
|
||||||
|
colors[3*i+2] = int(colorData[3*i+2] * amplitude)
|
||||||
|
|
||||||
|
# set colors
|
||||||
|
hyperion.setColor(colors)
|
||||||
|
|
||||||
|
# increment the phase
|
||||||
|
amplitudePhase = (amplitudePhase + amplitudePhaseIncrement) % (2*math.pi)
|
||||||
|
|
||||||
|
if rotateColors:
|
||||||
|
colorData = colorData[-colorDataIncrement:] + colorData[:-colorDataIncrement]
|
||||||
|
rotateColors = not rotateColors
|
||||||
|
|
||||||
|
# sleep for a while
|
||||||
|
time.sleep(sleepTime)
|
10
effects/rainbow-mood.json
Executable file
10
effects/rainbow-mood.json
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name" : "Rainbow mood",
|
||||||
|
"script" : "rainbow-mood.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"rotation-time" : 60.0,
|
||||||
|
"brightness" : 1.0,
|
||||||
|
"reverse" : false
|
||||||
|
}
|
||||||
|
}
|
30
effects/rainbow-mood.py
Normal file
30
effects/rainbow-mood.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import hyperion
|
||||||
|
import time
|
||||||
|
import colorsys
|
||||||
|
|
||||||
|
# Get the parameters
|
||||||
|
rotationTime = float(hyperion.args.get('rotation-time', 30.0))
|
||||||
|
brightness = float(hyperion.args.get('brightness', 1.0))
|
||||||
|
saturation = float(hyperion.args.get('saturation', 1.0))
|
||||||
|
reverse = bool(hyperion.args.get('reverse', False))
|
||||||
|
|
||||||
|
# Check parameters
|
||||||
|
rotationTime = max(0.1, rotationTime)
|
||||||
|
brightness = max(0.0, min(brightness, 1.0))
|
||||||
|
saturation = max(0.0, min(saturation, 1.0))
|
||||||
|
|
||||||
|
# Calculate the sleep time and hue increment
|
||||||
|
sleepTime = 0.1
|
||||||
|
hueIncrement = sleepTime / rotationTime
|
||||||
|
|
||||||
|
# Switch direction if needed
|
||||||
|
if reverse:
|
||||||
|
increment = -increment
|
||||||
|
|
||||||
|
# Start the write data loop
|
||||||
|
hue = 0.0
|
||||||
|
while not hyperion.abort():
|
||||||
|
rgb = colorsys.hsv_to_rgb(hue, saturation, brightness)
|
||||||
|
hyperion.setColor(int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2]))
|
||||||
|
hue = (hue + hueIncrement) % 1.0
|
||||||
|
time.sleep(sleepTime)
|
10
effects/rainbow-swirl-fast.json
Executable file
10
effects/rainbow-swirl-fast.json
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name" : "Rainbow swirl fast",
|
||||||
|
"script" : "rainbow-swirl.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"rotation-time" : 3.0,
|
||||||
|
"brightness" : 1.0,
|
||||||
|
"reverse" : false
|
||||||
|
}
|
||||||
|
}
|
10
effects/rainbow-swirl.json
Executable file
10
effects/rainbow-swirl.json
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name" : "Rainbow swirl",
|
||||||
|
"script" : "rainbow-swirl.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"rotation-time" : 20.0,
|
||||||
|
"brightness" : 1.0,
|
||||||
|
"reverse" : false
|
||||||
|
}
|
||||||
|
}
|
39
effects/rainbow-swirl.py
Normal file
39
effects/rainbow-swirl.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import hyperion
|
||||||
|
import time
|
||||||
|
import colorsys
|
||||||
|
|
||||||
|
# Get the parameters
|
||||||
|
rotationTime = float(hyperion.args.get('rotation-time', 3.0))
|
||||||
|
brightness = float(hyperion.args.get('brightness', 1.0))
|
||||||
|
saturation = float(hyperion.args.get('saturation', 1.0))
|
||||||
|
reverse = bool(hyperion.args.get('reverse', False))
|
||||||
|
|
||||||
|
# Check parameters
|
||||||
|
rotationTime = max(0.1, rotationTime)
|
||||||
|
brightness = max(0.0, min(brightness, 1.0))
|
||||||
|
saturation = max(0.0, min(saturation, 1.0))
|
||||||
|
|
||||||
|
# Initialize the led data
|
||||||
|
ledData = bytearray()
|
||||||
|
for i in range(hyperion.ledCount):
|
||||||
|
hue = float(i)/hyperion.ledCount
|
||||||
|
rgb = colorsys.hsv_to_rgb(hue, saturation, brightness)
|
||||||
|
ledData += bytearray((int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2])))
|
||||||
|
|
||||||
|
# Calculate the sleep time and rotation increment
|
||||||
|
increment = 3
|
||||||
|
sleepTime = rotationTime / hyperion.ledCount
|
||||||
|
while sleepTime < 0.05:
|
||||||
|
increment *= 2
|
||||||
|
sleepTime *= 2
|
||||||
|
increment %= hyperion.ledCount
|
||||||
|
|
||||||
|
# Switch direction if needed
|
||||||
|
if reverse:
|
||||||
|
increment = -increment
|
||||||
|
|
||||||
|
# Start the write data loop
|
||||||
|
while not hyperion.abort():
|
||||||
|
hyperion.setColor(ledData)
|
||||||
|
ledData = ledData[-increment:] + ledData[:-increment]
|
||||||
|
time.sleep(sleepTime)
|
8
effects/strobe.json
Normal file
8
effects/strobe.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name" : "Stroboscope",
|
||||||
|
"script" : "strobe.py",
|
||||||
|
"args" :
|
||||||
|
{
|
||||||
|
"frequency" : 10.0
|
||||||
|
}
|
||||||
|
}
|
23
effects/strobe.py
Normal file
23
effects/strobe.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import hyperion
|
||||||
|
import time
|
||||||
|
import colorsys
|
||||||
|
|
||||||
|
# Get the rotation time
|
||||||
|
frequency = float(hyperion.args.get('frequency', 10.0))
|
||||||
|
|
||||||
|
# Check parameters
|
||||||
|
frequency = min(100.0, frequency)
|
||||||
|
|
||||||
|
# Compute the strobe interval
|
||||||
|
sleepTime = 1.0 / frequency
|
||||||
|
|
||||||
|
# Initialize the led data
|
||||||
|
blackLedsData = bytearray(hyperion.ledCount * ( 0, 0, 0))
|
||||||
|
whiteLedsData = bytearray(hyperion.ledCount * (255,255,255))
|
||||||
|
|
||||||
|
# Start the write data loop
|
||||||
|
while not hyperion.abort():
|
||||||
|
hyperion.setColor(blackLedsData)
|
||||||
|
time.sleep(sleepTime)
|
||||||
|
hyperion.setColor(whiteLedsData)
|
||||||
|
time.sleep(sleepTime)
|
@ -1,24 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Pure virtual base class (or interface) for boot sequences. A BootSequence is started after the
|
|
||||||
/// Hyperion deamon is started to demonstrate the proper functioninf of the attached leds (and lets
|
|
||||||
/// face it because it is cool)
|
|
||||||
///
|
|
||||||
class BootSequence
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Empty virtual destructor for abstract base class
|
|
||||||
///
|
|
||||||
virtual ~BootSequence()
|
|
||||||
{
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Starts the boot sequence writing one or more colors to the attached leds
|
|
||||||
///
|
|
||||||
virtual void start() = 0;
|
|
||||||
};
|
|
@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// Jsoncpp includes
|
|
||||||
#include <json/json.h>
|
|
||||||
|
|
||||||
// Bootsequence includes
|
|
||||||
#include <bootsequence/BootSequence.h>
|
|
||||||
|
|
||||||
// Hyperion includes
|
|
||||||
#include <hyperion/Hyperion.h>
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Factory for settings based construction of a boot-sequence
|
|
||||||
///
|
|
||||||
class BootSequenceFactory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Creates a BootSequence using the given configuration (and Hyperion connection). Ownship of
|
|
||||||
/// the returned instance is transferred
|
|
||||||
///
|
|
||||||
/// @param[in] hyperion The Hyperion controlling the leds
|
|
||||||
/// @param[in] jsonConfig The boot-sequence configuration
|
|
||||||
///
|
|
||||||
/// @return The bootsequence (ownership is transferred to the caller
|
|
||||||
///
|
|
||||||
static BootSequence * createBootSequence(Hyperion * hyperion, const Json::Value & jsonConfig);
|
|
||||||
};
|
|
14
include/effectengine/EffectDefinition.h
Normal file
14
include/effectengine/EffectDefinition.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// stl include
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// json include
|
||||||
|
#include <json/value.h>
|
||||||
|
|
||||||
|
struct EffectDefinition
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::string script;
|
||||||
|
Json::Value args;
|
||||||
|
};
|
59
include/effectengine/EffectEngine.h
Normal file
59
include/effectengine/EffectEngine.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
// Json includes
|
||||||
|
#include <json/value.h>
|
||||||
|
|
||||||
|
// Hyperion includes
|
||||||
|
#include <hyperion/Hyperion.h>
|
||||||
|
|
||||||
|
// Effect engine includes
|
||||||
|
#include <effectengine/EffectDefinition.h>
|
||||||
|
|
||||||
|
// pre-declarioation
|
||||||
|
class Effect;
|
||||||
|
typedef struct _ts PyThreadState;
|
||||||
|
|
||||||
|
class EffectEngine : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectConfig);
|
||||||
|
virtual ~EffectEngine();
|
||||||
|
|
||||||
|
const std::list<EffectDefinition> & getEffects() const;
|
||||||
|
|
||||||
|
static bool loadEffectDefinition(const std::string & path, const std::string & effectConfigFile, EffectDefinition &effectDefinition);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
/// Run the specified effect on the given priority channel and optionally specify a timeout
|
||||||
|
int runEffect(const std::string &effectName, int priority, int timeout = -1);
|
||||||
|
|
||||||
|
/// Run the specified effect on the given priority channel and optionally specify a timeout
|
||||||
|
int runEffect(const std::string &effectName, const Json::Value & args, int priority, int timeout = -1);
|
||||||
|
|
||||||
|
/// Clear any effect running on the provided channel
|
||||||
|
void channelCleared(int priority);
|
||||||
|
|
||||||
|
/// Clear all effects
|
||||||
|
void allChannelsCleared();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void effectFinished(Effect * effect);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Run the specified effect on the given priority channel and optionally specify a timeout
|
||||||
|
int runEffectScript(const std::string &script, const Json::Value & args, int priority, int timeout = -1);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Hyperion * _hyperion;
|
||||||
|
|
||||||
|
std::list<EffectDefinition> _availableEffects;
|
||||||
|
|
||||||
|
std::list<Effect *> _activeEffects;
|
||||||
|
|
||||||
|
PyThreadState * _mainThreadState;
|
||||||
|
};
|
@ -15,8 +15,12 @@
|
|||||||
#include <hyperion/LedDevice.h>
|
#include <hyperion/LedDevice.h>
|
||||||
#include <hyperion/PriorityMuxer.h>
|
#include <hyperion/PriorityMuxer.h>
|
||||||
|
|
||||||
|
// Effect engine includes
|
||||||
|
#include <effectengine/EffectDefinition.h>
|
||||||
|
|
||||||
// Forward class declaration
|
// Forward class declaration
|
||||||
class ColorTransform;
|
class ColorTransform;
|
||||||
|
class EffectEngine;
|
||||||
class HsvTransform;
|
class HsvTransform;
|
||||||
class RgbChannelTransform;
|
class RgbChannelTransform;
|
||||||
class MultiColorTransform;
|
class MultiColorTransform;
|
||||||
@ -71,6 +75,29 @@ public:
|
|||||||
///
|
///
|
||||||
unsigned getLedCount() const;
|
unsigned getLedCount() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns a list of active priorities
|
||||||
|
///
|
||||||
|
/// @return The list with priorities
|
||||||
|
///
|
||||||
|
QList<int> getActivePriorities() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns the information of a specific priorrity channel
|
||||||
|
///
|
||||||
|
/// @param[in] priority The priority channel
|
||||||
|
///
|
||||||
|
/// @return The information of the given
|
||||||
|
///
|
||||||
|
/// @throw std::runtime_error when the priority channel does not exist
|
||||||
|
///
|
||||||
|
const InputInfo& getPriorityInfo(const int priority) const;
|
||||||
|
|
||||||
|
/// Get the list of available effects
|
||||||
|
/// @return The list of available effects
|
||||||
|
const std::list<EffectDefinition> &getEffects() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
///
|
///
|
||||||
/// Writes a single color to all the leds for the given time and priority
|
/// Writes a single color to all the leds for the given time and priority
|
||||||
///
|
///
|
||||||
@ -78,7 +105,7 @@ public:
|
|||||||
/// @param[in] ledColor The color to write to the leds
|
/// @param[in] ledColor The color to write to the leds
|
||||||
/// @param[in] timeout_ms The time the leds are set to the given color [ms]
|
/// @param[in] timeout_ms The time the leds are set to the given color [ms]
|
||||||
///
|
///
|
||||||
void setColor(int priority, const ColorRgb &ledColor, const int timeout_ms);
|
void setColor(int priority, const ColorRgb &ledColor, const int timeout_ms, bool clearEffects = true);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Writes the given colors to all leds for the given time and priority
|
/// Writes the given colors to all leds for the given time and priority
|
||||||
@ -87,7 +114,7 @@ public:
|
|||||||
/// @param[in] ledColors The colors to write to the leds
|
/// @param[in] ledColors The colors to write to the leds
|
||||||
/// @param[in] timeout_ms The time the leds are set to the given colors [ms]
|
/// @param[in] timeout_ms The time the leds are set to the given colors [ms]
|
||||||
///
|
///
|
||||||
void setColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms);
|
void setColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms, bool clearEffects = true);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns the list with unique transform identifiers
|
/// Returns the list with unique transform identifiers
|
||||||
@ -117,24 +144,20 @@ public:
|
|||||||
///
|
///
|
||||||
void clearall();
|
void clearall();
|
||||||
|
|
||||||
///
|
/// Run the specified effect on the given priority channel and optionally specify a timeout
|
||||||
/// Returns a list of active priorities
|
/// @param effectName Name of the effec to run
|
||||||
///
|
/// @param priority The priority channel of the effect
|
||||||
/// @return The list with priorities
|
/// @param timout The timeout of the effect (after the timout, the effect will be cleared)
|
||||||
///
|
int setEffect(const std::string & effectName, int priority, int timeout = -1);
|
||||||
QList<int> getActivePriorities() const;
|
|
||||||
|
|
||||||
///
|
/// Run the specified effect on the given priority channel and optionally specify a timeout
|
||||||
/// Returns the information of a specific priorrity channel
|
/// @param effectName Name of the effec to run
|
||||||
///
|
/// @param args arguments of the effect script
|
||||||
/// @param[in] priority The priority channel
|
/// @param priority The priority channel of the effect
|
||||||
///
|
/// @param timout The timeout of the effect (after the timout, the effect will be cleared)
|
||||||
/// @return The information of the given
|
int setEffect(const std::string & effectName, const Json::Value & args, int priority, int timeout = -1);
|
||||||
///
|
|
||||||
/// @throw std::runtime_error when the priority channel does not exist
|
|
||||||
///
|
|
||||||
const InputInfo& getPriorityInfo(const int priority) const;
|
|
||||||
|
|
||||||
|
public:
|
||||||
static LedDevice * createDevice(const Json::Value & deviceConfig);
|
static LedDevice * createDevice(const Json::Value & deviceConfig);
|
||||||
static ColorOrder createColorOrder(const Json::Value & deviceConfig);
|
static ColorOrder createColorOrder(const Json::Value & deviceConfig);
|
||||||
static LedString createLedString(const Json::Value & ledsConfig);
|
static LedString createLedString(const Json::Value & ledsConfig);
|
||||||
@ -146,6 +169,15 @@ public:
|
|||||||
|
|
||||||
static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice);
|
static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/// Signal which is emitted when a priority channel is actively cleared
|
||||||
|
/// This signal will not be emitted when a priority channel time out
|
||||||
|
void channelCleared(int priority);
|
||||||
|
|
||||||
|
/// Signal which is emitted when all priority channels are actively cleared
|
||||||
|
/// This signal will not be emitted when a priority channel time out
|
||||||
|
void allChannelsCleared();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
///
|
///
|
||||||
/// Updates the priority muxer with the current time and (re)writes the led color with applied
|
/// Updates the priority muxer with the current time and (re)writes the led color with applied
|
||||||
@ -169,6 +201,9 @@ private:
|
|||||||
/// The actual LedDevice
|
/// The actual LedDevice
|
||||||
LedDevice * _device;
|
LedDevice * _device;
|
||||||
|
|
||||||
|
/// Effect engine
|
||||||
|
EffectEngine * _effectEngine;
|
||||||
|
|
||||||
/// The timer for handling priority channel timeouts
|
/// The timer for handling priority channel timeouts
|
||||||
QTimer _timer;
|
QTimer _timer;
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,11 @@ class ImageProcessor
|
|||||||
public:
|
public:
|
||||||
~ImageProcessor();
|
~ImageProcessor();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns the number of attached leds
|
||||||
|
///
|
||||||
|
unsigned getLedCount() const;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Specifies the width and height of 'incomming' images. This will resize the buffer-image to
|
/// Specifies the width and height of 'incomming' images. This will resize the buffer-image to
|
||||||
/// match the given size.
|
/// match the given size.
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include)
|
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include)
|
||||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc)
|
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc)
|
||||||
|
|
||||||
add_subdirectory(bootsequence)
|
|
||||||
add_subdirectory(hyperion)
|
add_subdirectory(hyperion)
|
||||||
add_subdirectory(jsonserver)
|
add_subdirectory(jsonserver)
|
||||||
add_subdirectory(protoserver)
|
add_subdirectory(protoserver)
|
||||||
add_subdirectory(boblightserver)
|
add_subdirectory(boblightserver)
|
||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
add_subdirectory(xbmcvideochecker)
|
add_subdirectory(xbmcvideochecker)
|
||||||
|
add_subdirectory(effectengine)
|
||||||
|
|
||||||
if (ENABLE_DISPMANX)
|
if (ENABLE_DISPMANX)
|
||||||
add_subdirectory(dispmanx-grabber)
|
add_subdirectory(dispmanx-grabber)
|
||||||
|
@ -133,7 +133,8 @@ void BoblightClientConnection::handleMessage(const QString & message)
|
|||||||
if (messageParts[3] == "rgb" && messageParts.size() == 7)
|
if (messageParts[3] == "rgb" && messageParts.size() == 7)
|
||||||
{
|
{
|
||||||
bool rc1, rc2, rc3;
|
bool rc1, rc2, rc3;
|
||||||
uint8_t red = 255 * messageParts[4].toFloat(&rc1);
|
uint8_t red = qMax(0, qMin(255, int(255 * messageParts[4].toFloat(&rc1))));
|
||||||
|
|
||||||
if (!rc1)
|
if (!rc1)
|
||||||
{
|
{
|
||||||
// maybe a locale issue. switch to a locale with a comma instead of a dot as decimal seperator (or vice versa)
|
// maybe a locale issue. switch to a locale with a comma instead of a dot as decimal seperator (or vice versa)
|
||||||
@ -141,11 +142,11 @@ void BoblightClientConnection::handleMessage(const QString & message)
|
|||||||
_locale.setNumberOptions(QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
|
_locale.setNumberOptions(QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
|
||||||
|
|
||||||
// try again
|
// try again
|
||||||
red = 255 * messageParts[4].toFloat(&rc1);
|
red = qMax(0, qMin(255, int(255 * messageParts[4].toFloat(&rc1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t green = 255 * messageParts[5].toFloat(&rc2);
|
uint8_t green = qMax(0, qMin(255, 255 * int(messageParts[5].toFloat(&rc2))));
|
||||||
uint8_t blue = 255 * messageParts[6].toFloat(&rc3);
|
uint8_t blue = qMax(0, qMin(255, 255 * int(messageParts[6].toFloat(&rc3))));
|
||||||
|
|
||||||
if (rc1 && rc2 && rc3)
|
if (rc1 && rc2 && rc3)
|
||||||
{
|
{
|
||||||
@ -153,6 +154,13 @@ void BoblightClientConnection::handleMessage(const QString & message)
|
|||||||
rgb.red = red;
|
rgb.red = red;
|
||||||
rgb.green = green;
|
rgb.green = green;
|
||||||
rgb.blue = blue;
|
rgb.blue = blue;
|
||||||
|
|
||||||
|
// send current color values to hyperion if this is the last led assuming leds values are send in order of id
|
||||||
|
if ((ledIndex == _ledColors.size() -1) && _priority < 255)
|
||||||
|
{
|
||||||
|
_hyperion->setColors(_priority, _ledColors, -1);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
#include "AbstractBootSequence.h"
|
|
||||||
|
|
||||||
AbstractBootSequence::AbstractBootSequence(Hyperion * hyperion, const int64_t interval, const unsigned iterationCnt) :
|
|
||||||
_timer(),
|
|
||||||
_hyperion(hyperion),
|
|
||||||
_priority(0),
|
|
||||||
_iterationCounter(iterationCnt)
|
|
||||||
{
|
|
||||||
_timer.setInterval(interval);
|
|
||||||
_timer.setSingleShot(false);
|
|
||||||
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractBootSequence::start()
|
|
||||||
{
|
|
||||||
_timer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractBootSequence::update()
|
|
||||||
{
|
|
||||||
if (_iterationCounter == 0)
|
|
||||||
{
|
|
||||||
_timer.stop();
|
|
||||||
_hyperion->clear(_priority);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtain the next led-colors from the child-class
|
|
||||||
const std::vector<ColorRgb>& colors = nextColors();
|
|
||||||
// Write the colors to hyperion
|
|
||||||
_hyperion->setColors(_priority, colors, -1);
|
|
||||||
|
|
||||||
// Decrease the loop count
|
|
||||||
--_iterationCounter;
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// QT includes
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
// Bootsequence includes
|
|
||||||
#include <bootsequence/BootSequence.h>
|
|
||||||
|
|
||||||
// Hyperion includes
|
|
||||||
#include <hyperion/Hyperion.h>
|
|
||||||
|
|
||||||
///
|
|
||||||
/// The AbstractBootSequence is an 'abstract' implementation of the BootSequence that handles the
|
|
||||||
/// event generation and Hyperion connection. Subclasses only need to specify the interval and
|
|
||||||
/// return the colors for the leds for each iteration.
|
|
||||||
///
|
|
||||||
class AbstractBootSequence : public QObject, public BootSequence
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
///
|
|
||||||
/// Constructs the AbstractBootSequence with the given parameters
|
|
||||||
///
|
|
||||||
/// @param hyperion The Hyperion instance
|
|
||||||
/// @param interval The interval between new led colors
|
|
||||||
/// @param iterationCnt The number of iteration performed by the boot sequence
|
|
||||||
///
|
|
||||||
AbstractBootSequence(Hyperion * hyperion, const int64_t interval, const unsigned iterationCnt);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Starts the boot-sequence
|
|
||||||
///
|
|
||||||
virtual void start();
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
///
|
|
||||||
/// Timer slot for handling each interval of the boot-sequence
|
|
||||||
///
|
|
||||||
void update();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
///
|
|
||||||
/// Child-classes must implement this by returning the next led colors in the sequence
|
|
||||||
///
|
|
||||||
/// @return The next led colors in the boot sequence
|
|
||||||
///
|
|
||||||
virtual const std::vector<ColorRgb>& nextColors() = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// The timer used to generate an 'update' signal every interval
|
|
||||||
QTimer _timer;
|
|
||||||
|
|
||||||
/// The Hyperion instance
|
|
||||||
Hyperion * _hyperion;
|
|
||||||
/// The priority of the boot sequence
|
|
||||||
int _priority;
|
|
||||||
|
|
||||||
/// The counter of the number of iterations left
|
|
||||||
int _iterationCounter;
|
|
||||||
};
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
|||||||
// stl includes
|
|
||||||
#include <cctype>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
// Bootsequence includes
|
|
||||||
#include <bootsequence/BootSequenceFactory.h>
|
|
||||||
|
|
||||||
// Local Bootsequence includes
|
|
||||||
#include "RainbowBootSequence.h"
|
|
||||||
#include "KittBootSequence.h"
|
|
||||||
|
|
||||||
BootSequence * BootSequenceFactory::createBootSequence(Hyperion * hyperion, const Json::Value & jsonConfig)
|
|
||||||
{
|
|
||||||
std::string type = jsonConfig["type"].asString();
|
|
||||||
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
|
||||||
|
|
||||||
if (type == "none")
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
else if (type == "rainbow")
|
|
||||||
{
|
|
||||||
std::cout << "SELECTED BOOT SEQUENCE: " << "RAINBOW" << std::endl;
|
|
||||||
const unsigned duration_ms = jsonConfig["duration_ms"].asUInt();
|
|
||||||
return new RainbowBootSequence(hyperion, duration_ms);
|
|
||||||
}
|
|
||||||
else if (type == "knightrider" || type == "knight rider" || "knight_rider")
|
|
||||||
{
|
|
||||||
std::cout << "SELECTED BOOT SEQUENCE: " << "KITT" << std::endl;
|
|
||||||
const unsigned duration_ms = jsonConfig["duration_ms"].asUInt();
|
|
||||||
return new KittBootSequence(hyperion, duration_ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cerr << "Unknown boot-sequence selected; boot-sequence disabled." << std::endl;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
|
|
||||||
# Define the current source locations
|
|
||||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/bootsequence)
|
|
||||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/bootsequence)
|
|
||||||
|
|
||||||
# Group the headers that go through the MOC compiler
|
|
||||||
SET(BootsequenceQT_HEADERS
|
|
||||||
${CURRENT_SOURCE_DIR}/AbstractBootSequence.h
|
|
||||||
)
|
|
||||||
|
|
||||||
SET(BootsequenceHEADERS
|
|
||||||
${CURRENT_HEADER_DIR}/BootSequence.h
|
|
||||||
${CURRENT_HEADER_DIR}/BootSequenceFactory.h
|
|
||||||
${CURRENT_SOURCE_DIR}/RainbowBootSequence.h
|
|
||||||
${CURRENT_SOURCE_DIR}/KittBootSequence.h
|
|
||||||
)
|
|
||||||
|
|
||||||
SET(BootsequenceSOURCES
|
|
||||||
${CURRENT_SOURCE_DIR}/AbstractBootSequence.cpp
|
|
||||||
${CURRENT_SOURCE_DIR}/BootSequenceFactory.cpp
|
|
||||||
${CURRENT_SOURCE_DIR}/RainbowBootSequence.cpp
|
|
||||||
${CURRENT_SOURCE_DIR}/KittBootSequence.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
QT4_WRAP_CPP(BootsequenceHEADERS_MOC ${BootsequenceQT_HEADERS})
|
|
||||||
|
|
||||||
add_library(bootsequence
|
|
||||||
${BootsequenceHEADERS}
|
|
||||||
${BootsequenceQT_HEADERS}
|
|
||||||
${BootsequenceHEADERS_MOC}
|
|
||||||
${BootsequenceSOURCES}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(bootsequence
|
|
||||||
hyperion
|
|
||||||
${QT_LIBRARIES})
|
|
@ -1,67 +0,0 @@
|
|||||||
|
|
||||||
// Hyperion includes
|
|
||||||
#include <hyperion/ImageProcessorFactory.h>
|
|
||||||
|
|
||||||
// Local-Bootsequence includes
|
|
||||||
#include "KittBootSequence.h"
|
|
||||||
|
|
||||||
KittBootSequence::KittBootSequence(Hyperion * hyperion, const unsigned duration_ms) :
|
|
||||||
AbstractBootSequence(hyperion, 100, duration_ms/100),
|
|
||||||
_processor(ImageProcessorFactory::getInstance().newImageProcessor()),
|
|
||||||
_image(9, 1, ColorRgb{0,0,0}),
|
|
||||||
_ledColors(hyperion->getLedCount(), ColorRgb{0,0,0}),
|
|
||||||
_forwardMove(false),
|
|
||||||
_currentLight(0)
|
|
||||||
{
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
KittBootSequence::~KittBootSequence()
|
|
||||||
{
|
|
||||||
delete _processor;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<ColorRgb>& KittBootSequence::nextColors()
|
|
||||||
{
|
|
||||||
|
|
||||||
// Switch the previous light 'off'
|
|
||||||
_image(_currentLight, 0) = ColorRgb{0,0,0};
|
|
||||||
|
|
||||||
// Move the current to the next light
|
|
||||||
moveNextLight();
|
|
||||||
|
|
||||||
// Switch the current light 'on'
|
|
||||||
_image(_currentLight, 0) = ColorRgb{255,0,0};
|
|
||||||
|
|
||||||
|
|
||||||
// Translate the 'image' to led colors
|
|
||||||
_processor->process(_image, _ledColors);
|
|
||||||
|
|
||||||
// Return the colors
|
|
||||||
return _ledColors;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KittBootSequence::moveNextLight()
|
|
||||||
{
|
|
||||||
// Increase/Decrease the current light
|
|
||||||
if (_forwardMove)
|
|
||||||
{
|
|
||||||
++_currentLight;
|
|
||||||
if (_currentLight == _image.width())
|
|
||||||
{
|
|
||||||
_forwardMove = false;
|
|
||||||
--_currentLight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_currentLight == 0)
|
|
||||||
{
|
|
||||||
_forwardMove = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
--_currentLight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Bootsequence includes
|
|
||||||
#include "AbstractBootSequence.h"
|
|
||||||
|
|
||||||
// Hyperion includes
|
|
||||||
#include <hyperion/Hyperion.h>
|
|
||||||
#include <hyperion/ImageProcessor.h>
|
|
||||||
|
|
||||||
///
|
|
||||||
/// The KITT BootSequence is a boot sequence inspired by the Knight Rider car: Knight Industries Two
|
|
||||||
/// Thousand (aka KITT)
|
|
||||||
///
|
|
||||||
class KittBootSequence : public AbstractBootSequence
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
///
|
|
||||||
/// Constructs the KITT BootSequence
|
|
||||||
///
|
|
||||||
/// @param[in] hyperion The Hyperion instance
|
|
||||||
/// @param[in] duration_ms The length of the sequence [ms]
|
|
||||||
///
|
|
||||||
KittBootSequence(Hyperion * hyperion, const unsigned duration_ms);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Destructor, deletes the processor
|
|
||||||
///
|
|
||||||
virtual ~KittBootSequence();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the next led color sequence
|
|
||||||
///
|
|
||||||
/// @return The next colors for the leds
|
|
||||||
///
|
|
||||||
virtual const std::vector<ColorRgb>& nextColors();
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Image processor to compute led-colors from the image
|
|
||||||
ImageProcessor * _processor;
|
|
||||||
|
|
||||||
/// 1D-Image of the KITT-grill contains a single red pixel and the rest black
|
|
||||||
Image<ColorRgb> _image;
|
|
||||||
|
|
||||||
/// The vector with led-colors
|
|
||||||
std::vector<ColorRgb> _ledColors;
|
|
||||||
|
|
||||||
/// Direction the red-light is currently moving
|
|
||||||
bool _forwardMove;
|
|
||||||
/// The location of the current red-light
|
|
||||||
unsigned _currentLight;
|
|
||||||
|
|
||||||
/// Moves the current light to the next (increase or decrease depending on direction)
|
|
||||||
void moveNextLight();
|
|
||||||
};
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
|||||||
|
|
||||||
// Utils includes
|
|
||||||
#include <utils/HsvTransform.h>
|
|
||||||
|
|
||||||
// Local-Bootsequence include
|
|
||||||
#include "RainbowBootSequence.h"
|
|
||||||
|
|
||||||
RainbowBootSequence::RainbowBootSequence(Hyperion * hyperion, const unsigned duration_ms) :
|
|
||||||
AbstractBootSequence(hyperion, duration_ms/hyperion->getLedCount(), hyperion->getLedCount()),
|
|
||||||
_ledColors(hyperion->getLedCount())
|
|
||||||
{
|
|
||||||
for (unsigned iLed=0; iLed<hyperion->getLedCount(); ++iLed)
|
|
||||||
{
|
|
||||||
ColorRgb& color = _ledColors[iLed];
|
|
||||||
HsvTransform::hsv2rgb(iLed*360/hyperion->getLedCount(), 255, 255, color.red, color.green, color.blue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<ColorRgb>& RainbowBootSequence::nextColors()
|
|
||||||
{
|
|
||||||
// Rotate the colors left
|
|
||||||
const ColorRgb headColor = _ledColors.front();
|
|
||||||
for (unsigned i=1; i<_ledColors.size(); ++i)
|
|
||||||
{
|
|
||||||
_ledColors[i-1] = _ledColors[i];
|
|
||||||
}
|
|
||||||
_ledColors.back() = headColor;
|
|
||||||
|
|
||||||
return _ledColors;
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// QT includes
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
// Bootsequence include
|
|
||||||
#include "AbstractBootSequence.h"
|
|
||||||
|
|
||||||
///
|
|
||||||
/// The RainborBootSequence shows a 'rainbow' (all lights have a different color). The rainbow is
|
|
||||||
/// rotated over each led during the length of the sequence.
|
|
||||||
///
|
|
||||||
class RainbowBootSequence : public AbstractBootSequence
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
///
|
|
||||||
/// Constructs the rainbow boot-sequence. Hyperion is used for writing the led colors. The given
|
|
||||||
/// duration is the length of the sequence.
|
|
||||||
///
|
|
||||||
/// @param[in] hyperion The Hyperion instance
|
|
||||||
/// @param[in] duration_ms The length of the sequence [ms]
|
|
||||||
///
|
|
||||||
RainbowBootSequence(Hyperion * hyperion, const unsigned duration_ms);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
///
|
|
||||||
/// Moves the rainbow one led further
|
|
||||||
///
|
|
||||||
const std::vector<ColorRgb>& nextColors();
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// The current color of the boot sequence (the rainbow)
|
|
||||||
std::vector<ColorRgb> _ledColors;
|
|
||||||
/// The counter of the number of iterations left
|
|
||||||
int _iterationCounter;
|
|
||||||
};
|
|
||||||
|
|
44
libsrc/effectengine/CMakeLists.txt
Normal file
44
libsrc/effectengine/CMakeLists.txt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
find_package(PythonLibs REQUIRED)
|
||||||
|
|
||||||
|
include_directories(${PYTHON_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
# Define the current source locations
|
||||||
|
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/effectengine)
|
||||||
|
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/effectengine)
|
||||||
|
|
||||||
|
# Group the headers that go through the MOC compiler
|
||||||
|
SET(EffectEngineQT_HEADERS
|
||||||
|
${CURRENT_HEADER_DIR}/EffectEngine.h
|
||||||
|
${CURRENT_SOURCE_DIR}/Effect.h
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(EffectEngineHEADERS
|
||||||
|
${CURRENT_HEADER_DIR}/EffectDefinition.h
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(EffectEngineSOURCES
|
||||||
|
${CURRENT_SOURCE_DIR}/EffectEngine.cpp
|
||||||
|
${CURRENT_SOURCE_DIR}/Effect.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
set(EffectEngine_RESOURCES ${CURRENT_SOURCE_DIR}/EffectEngine.qrc)
|
||||||
|
|
||||||
|
QT4_WRAP_CPP(EffectEngineHEADERS_MOC ${EffectEngineQT_HEADERS})
|
||||||
|
|
||||||
|
qt4_add_resources(EffectEngine_RESOURCES_RCC ${EffectEngine_RESOURCES} OPTIONS "-no-compress")
|
||||||
|
|
||||||
|
add_library(effectengine
|
||||||
|
${EffectEngineHEADERS}
|
||||||
|
${EffectEngineQT_HEADERS}
|
||||||
|
${EffectEngineHEADERS_MOC}
|
||||||
|
${EffectEngine_RESOURCES_RCC}
|
||||||
|
${EffectEngineSOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(effectengine
|
||||||
|
hyperion
|
||||||
|
jsoncpp
|
||||||
|
${QT_LIBRARIES}
|
||||||
|
${PYTHON_LIBRARIES})
|
310
libsrc/effectengine/Effect.cpp
Normal file
310
libsrc/effectengine/Effect.cpp
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
// Python include
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
// stl includes
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
// effect engin eincludes
|
||||||
|
#include "Effect.h"
|
||||||
|
|
||||||
|
// Python method table
|
||||||
|
PyMethodDef Effect::effectMethods[] = {
|
||||||
|
{"setColor", Effect::wrapSetColor, METH_VARARGS, "Set a new color for the leds."},
|
||||||
|
{"setImage", Effect::wrapSetImage, METH_VARARGS, "Set a new image to process and determine new led colors."},
|
||||||
|
{"abort", Effect::wrapAbort, METH_NOARGS, "Check if the effect should abort execution."},
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Effect::Effect(int priority, int timeout, const std::string & script, const Json::Value & args) :
|
||||||
|
QThread(),
|
||||||
|
_priority(priority),
|
||||||
|
_timeout(timeout),
|
||||||
|
_script(script),
|
||||||
|
_args(args),
|
||||||
|
_endTime(-1),
|
||||||
|
_interpreterThreadState(nullptr),
|
||||||
|
_abortRequested(false),
|
||||||
|
_imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()),
|
||||||
|
_colors()
|
||||||
|
{
|
||||||
|
_colors.resize(_imageProcessor->getLedCount(), ColorRgb::BLACK);
|
||||||
|
|
||||||
|
// connect the finished signal
|
||||||
|
connect(this, SIGNAL(finished()), this, SLOT(effectFinished()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect::~Effect()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Effect::run()
|
||||||
|
{
|
||||||
|
// Initialize a new thread state
|
||||||
|
PyEval_AcquireLock(); // Get the GIL
|
||||||
|
_interpreterThreadState = Py_NewInterpreter();
|
||||||
|
|
||||||
|
// add methods extra builtin methods to the interpreter
|
||||||
|
PyObject * thisCapsule = PyCapsule_New(this, nullptr, nullptr);
|
||||||
|
PyObject * module = Py_InitModule4("hyperion", effectMethods, nullptr, thisCapsule, PYTHON_API_VERSION);
|
||||||
|
|
||||||
|
// add ledCount variable to the interpreter
|
||||||
|
PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _imageProcessor->getLedCount()));
|
||||||
|
|
||||||
|
// add a args variable to the interpreter
|
||||||
|
PyObject_SetAttrString(module, "args", json2python(_args));
|
||||||
|
//PyObject_SetAttrString(module, "args", Py_BuildValue("s", _args.c_str()));
|
||||||
|
|
||||||
|
// Set the end time if applicable
|
||||||
|
if (_timeout > 0)
|
||||||
|
{
|
||||||
|
_endTime = QDateTime::currentMSecsSinceEpoch() + _timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the effect script
|
||||||
|
FILE* file = fopen(_script.c_str(), "r");
|
||||||
|
if (file != nullptr)
|
||||||
|
{
|
||||||
|
PyRun_SimpleFile(file, _script.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Unable to open script file " << _script << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the thread state
|
||||||
|
Py_EndInterpreter(_interpreterThreadState);
|
||||||
|
_interpreterThreadState = nullptr;
|
||||||
|
PyEval_ReleaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Effect::getPriority() const
|
||||||
|
{
|
||||||
|
return _priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Effect::isAbortRequested() const
|
||||||
|
{
|
||||||
|
return _abortRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Effect::abort()
|
||||||
|
{
|
||||||
|
_abortRequested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Effect::effectFinished()
|
||||||
|
{
|
||||||
|
emit effectFinished(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *Effect::json2python(const Json::Value &json) const
|
||||||
|
{
|
||||||
|
switch (json.type())
|
||||||
|
{
|
||||||
|
case Json::nullValue:
|
||||||
|
return Py_BuildValue("");
|
||||||
|
case Json::realValue:
|
||||||
|
return Py_BuildValue("d", json.asDouble());
|
||||||
|
case Json::intValue:
|
||||||
|
case Json::uintValue:
|
||||||
|
return Py_BuildValue("i", json.asInt());
|
||||||
|
case Json::booleanValue:
|
||||||
|
return Py_BuildValue("i", json.asBool() ? 1 : 0);
|
||||||
|
case Json::stringValue:
|
||||||
|
return Py_BuildValue("s", json.asCString());
|
||||||
|
case Json::objectValue:
|
||||||
|
{
|
||||||
|
PyObject * obj = PyDict_New();
|
||||||
|
for (Json::Value::iterator i = json.begin(); i != json.end(); ++i)
|
||||||
|
{
|
||||||
|
PyDict_SetItemString(obj, i.memberName(), json2python(*i));
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
case Json::arrayValue:
|
||||||
|
{
|
||||||
|
PyObject * list = PyList_New(json.size());
|
||||||
|
for (Json::Value::iterator i = json.begin(); i != json.end(); ++i)
|
||||||
|
{
|
||||||
|
PyList_SetItem(list, i.index(), json2python(*i));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
// get the effect
|
||||||
|
Effect * effect = getEffect(self);
|
||||||
|
|
||||||
|
// check if we have aborted already
|
||||||
|
if (effect->_abortRequested)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine the timeout
|
||||||
|
int timeout = effect->_timeout;
|
||||||
|
if (timeout > 0)
|
||||||
|
{
|
||||||
|
timeout = effect->_endTime - QDateTime::currentMSecsSinceEpoch();
|
||||||
|
|
||||||
|
// we are done if the time has passed
|
||||||
|
if (timeout <= 0)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the number of arguments
|
||||||
|
int argCount = PyTuple_Size(args);
|
||||||
|
if (argCount == 3)
|
||||||
|
{
|
||||||
|
// three seperate arguments for red, green, and blue
|
||||||
|
ColorRgb color;
|
||||||
|
if (PyArg_ParseTuple(args, "bbb", &color.red, &color.green, &color.blue))
|
||||||
|
{
|
||||||
|
std::fill(effect->_colors.begin(), effect->_colors.end(), color);
|
||||||
|
effect->setColors(effect->_priority, effect->_colors, timeout, false);
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (argCount == 1)
|
||||||
|
{
|
||||||
|
// bytearray of values
|
||||||
|
PyObject * bytearray = nullptr;
|
||||||
|
if (PyArg_ParseTuple(args, "O", &bytearray))
|
||||||
|
{
|
||||||
|
if (PyByteArray_Check(bytearray))
|
||||||
|
{
|
||||||
|
size_t length = PyByteArray_Size(bytearray);
|
||||||
|
if (length == 3 * effect->_imageProcessor->getLedCount())
|
||||||
|
{
|
||||||
|
char * data = PyByteArray_AS_STRING(bytearray);
|
||||||
|
memcpy(effect->_colors.data(), data, length);
|
||||||
|
effect->setColors(effect->_priority, effect->_colors, timeout, false);
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Length of bytearray argument should be 3*ledCount");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Argument is not a bytearray");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Function expect 1 or 3 arguments");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// error
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Unknown error");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
// get the effect
|
||||||
|
Effect * effect = getEffect(self);
|
||||||
|
|
||||||
|
// check if we have aborted already
|
||||||
|
if (effect->_abortRequested)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine the timeout
|
||||||
|
int timeout = effect->_timeout;
|
||||||
|
if (timeout > 0)
|
||||||
|
{
|
||||||
|
timeout = effect->_endTime - QDateTime::currentMSecsSinceEpoch();
|
||||||
|
|
||||||
|
// we are done if the time has passed
|
||||||
|
if (timeout <= 0)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytearray of values
|
||||||
|
int width, height;
|
||||||
|
PyObject * bytearray = nullptr;
|
||||||
|
if (PyArg_ParseTuple(args, "iiO", &width, &height, &bytearray))
|
||||||
|
{
|
||||||
|
if (PyByteArray_Check(bytearray))
|
||||||
|
{
|
||||||
|
int length = PyByteArray_Size(bytearray);
|
||||||
|
if (length == 3 * width * height)
|
||||||
|
{
|
||||||
|
Image<ColorRgb> image(width, height);
|
||||||
|
char * data = PyByteArray_AS_STRING(bytearray);
|
||||||
|
memcpy(image.memptr(), data, length);
|
||||||
|
|
||||||
|
effect->_imageProcessor->process(image, effect->_colors);
|
||||||
|
effect->setColors(effect->_priority, effect->_colors, timeout, false);
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Length of bytearray argument should be 3*ledCount");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Argument 3 is not a bytearray");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// error
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Unknown error");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
|
||||||
|
{
|
||||||
|
Effect * effect = getEffect(self);
|
||||||
|
|
||||||
|
// Test if the effect has reached it end time
|
||||||
|
if (effect->_timeout > 0 && QDateTime::currentMSecsSinceEpoch() > effect->_endTime)
|
||||||
|
{
|
||||||
|
effect->_abortRequested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Py_BuildValue("i", effect->_abortRequested ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect * Effect::getEffect(PyObject *self)
|
||||||
|
{
|
||||||
|
// Get the effect from the capsule in the self pointer
|
||||||
|
return reinterpret_cast<Effect *>(PyCapsule_GetPointer(self, nullptr));
|
||||||
|
}
|
67
libsrc/effectengine/Effect.h
Normal file
67
libsrc/effectengine/Effect.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Python includes
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
// Hyperion includes
|
||||||
|
#include <hyperion/ImageProcessor.h>
|
||||||
|
|
||||||
|
class Effect : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Effect(int priority, int timeout, const std::string & script, const Json::Value & args = Json::Value());
|
||||||
|
virtual ~Effect();
|
||||||
|
|
||||||
|
virtual void run();
|
||||||
|
|
||||||
|
int getPriority() const;
|
||||||
|
|
||||||
|
bool isAbortRequested() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void abort();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void effectFinished(Effect * effect);
|
||||||
|
|
||||||
|
void setColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms, bool clearEffects);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void effectFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PyObject * json2python(const Json::Value & json) const;
|
||||||
|
|
||||||
|
// Wrapper methods for Python interpreter extra buildin methods
|
||||||
|
static PyMethodDef effectMethods[];
|
||||||
|
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
|
||||||
|
static PyObject* wrapSetImage(PyObject *self, PyObject *args);
|
||||||
|
static PyObject* wrapAbort(PyObject *self, PyObject *args);
|
||||||
|
static Effect * getEffect(PyObject *self);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int _priority;
|
||||||
|
|
||||||
|
const int _timeout;
|
||||||
|
|
||||||
|
const std::string _script;
|
||||||
|
|
||||||
|
const Json::Value _args;
|
||||||
|
|
||||||
|
int64_t _endTime;
|
||||||
|
|
||||||
|
PyThreadState * _interpreterThreadState;
|
||||||
|
|
||||||
|
bool _abortRequested;
|
||||||
|
|
||||||
|
/// The processor for translating images to led-values
|
||||||
|
ImageProcessor * _imageProcessor;
|
||||||
|
|
||||||
|
/// Buffer for colorData
|
||||||
|
std::vector<ColorRgb> _colors;
|
||||||
|
};
|
342
libsrc/effectengine/EffectDefinition.schema.json
Normal file
342
libsrc/effectengine/EffectDefinition.schema.json
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : true,
|
||||||
|
"properties" : {
|
||||||
|
"device" : {
|
||||||
|
"type" : "object",
|
||||||
|
"required" : true,
|
||||||
|
"properties" : {
|
||||||
|
"name" : {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"type" : {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"output" : {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"rate" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : true,
|
||||||
|
"minimum" : 0
|
||||||
|
},
|
||||||
|
"colorOrder" : {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : false
|
||||||
|
},
|
||||||
|
"bgr-output" : { // deprecated
|
||||||
|
"type" : "boolean",
|
||||||
|
"required" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"type":"object",
|
||||||
|
"required":false,
|
||||||
|
"properties": {
|
||||||
|
"hsv" : {
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"saturationGain" : {
|
||||||
|
"type" : "number",
|
||||||
|
"required" : false,
|
||||||
|
"minimum" : 0.0
|
||||||
|
},
|
||||||
|
"valueGain" : {
|
||||||
|
"type" : "number",
|
||||||
|
"required" : false,
|
||||||
|
"minimum" : 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"red": {
|
||||||
|
"type":"object",
|
||||||
|
"required":false,
|
||||||
|
"properties":{
|
||||||
|
"gamma": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false
|
||||||
|
},
|
||||||
|
"blacklevel": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false
|
||||||
|
},
|
||||||
|
"whitelevel": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false
|
||||||
|
},
|
||||||
|
"threshold": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false,
|
||||||
|
"minimum" : 0.0,
|
||||||
|
"maximum" : 1.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"green": {
|
||||||
|
"type":"object",
|
||||||
|
"required":false,
|
||||||
|
"properties":{
|
||||||
|
"gamma": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false
|
||||||
|
},
|
||||||
|
"blacklevel": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false
|
||||||
|
},
|
||||||
|
"whitelevel": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false
|
||||||
|
},
|
||||||
|
"threshold": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false,
|
||||||
|
"minimum" : 0.0,
|
||||||
|
"maximum" : 1.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"blue": {
|
||||||
|
"type":"object",
|
||||||
|
"required":false,
|
||||||
|
"properties":{
|
||||||
|
"gamma": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false
|
||||||
|
},
|
||||||
|
"whitelevel": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false
|
||||||
|
},
|
||||||
|
"blacklevel": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false
|
||||||
|
},
|
||||||
|
"threshold": {
|
||||||
|
"type":"number",
|
||||||
|
"required":false,
|
||||||
|
"minimum" : 0.0,
|
||||||
|
"maximum" : 1.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"smoothing" : {
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"type" : {
|
||||||
|
"type" : "enum",
|
||||||
|
"required" : true,
|
||||||
|
"values" : ["none", "linear"]
|
||||||
|
},
|
||||||
|
"time_ms" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : false,
|
||||||
|
"minimum" : 10
|
||||||
|
},
|
||||||
|
"updateFrequency" : {
|
||||||
|
"type" : "number",
|
||||||
|
"required" : false,
|
||||||
|
"minimum" : 0.001
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"leds": {
|
||||||
|
"type":"array",
|
||||||
|
"required":true,
|
||||||
|
"items": {
|
||||||
|
"type":"object",
|
||||||
|
"properties": {
|
||||||
|
"index": {
|
||||||
|
"type":"integer",
|
||||||
|
"required":true
|
||||||
|
},
|
||||||
|
"hscan": {
|
||||||
|
"type":"object",
|
||||||
|
"required":true,
|
||||||
|
"properties": {
|
||||||
|
"minimum": {
|
||||||
|
"type":"number",
|
||||||
|
"required":true
|
||||||
|
},
|
||||||
|
"maximum": {
|
||||||
|
"type":"number",
|
||||||
|
"required":true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"vscan": {
|
||||||
|
"type":"object",
|
||||||
|
"required":true,
|
||||||
|
"properties": {
|
||||||
|
"minimum": {
|
||||||
|
"type":"number",
|
||||||
|
"required":true
|
||||||
|
},
|
||||||
|
"maximum": {
|
||||||
|
"type":"number",
|
||||||
|
"required":true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"effects" :
|
||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"paths" : {
|
||||||
|
"type" : "array",
|
||||||
|
"required" : false,
|
||||||
|
"items" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"blackborderdetector" :
|
||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"enable" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"required" : true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"xbmcVideoChecker" :
|
||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"xbmcAddress" : {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"xbmcTcpPort" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"grabVideo" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"grabPictures" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"grabAudio" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"grabMenu" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"required" : true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"bootsequence" :
|
||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"path" : {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"effect" : {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"framegrabber" :
|
||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"width" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"height" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"frequency_Hz" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"jsonServer" :
|
||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"port" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : true,
|
||||||
|
"minimum" : 0,
|
||||||
|
"maximum" : 65535
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"protoServer" :
|
||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"port" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : true,
|
||||||
|
"minimum" : 0,
|
||||||
|
"maximum" : 65535
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
|
"boblightServer" :
|
||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"port" : {
|
||||||
|
"type" : "integer",
|
||||||
|
"required" : true,
|
||||||
|
"minimum" : 0,
|
||||||
|
"maximum" : 65535
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
}
|
204
libsrc/effectengine/EffectEngine.cpp
Normal file
204
libsrc/effectengine/EffectEngine.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
// Python includes
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
// Stl includes
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QResource>
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
// hyperion util includes
|
||||||
|
#include <utils/jsonschema/JsonSchemaChecker.h>
|
||||||
|
|
||||||
|
// effect engine includes
|
||||||
|
#include <effectengine/EffectEngine.h>
|
||||||
|
#include "Effect.h"
|
||||||
|
|
||||||
|
EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectConfig) :
|
||||||
|
_hyperion(hyperion),
|
||||||
|
_availableEffects(),
|
||||||
|
_activeEffects(),
|
||||||
|
_mainThreadState(nullptr)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<std::vector<ColorRgb>>("std::vector<ColorRgb>");
|
||||||
|
|
||||||
|
// connect the Hyperion channel clear feedback
|
||||||
|
connect(_hyperion, SIGNAL(channelCleared(int)), this, SLOT(channelCleared(int)));
|
||||||
|
connect(_hyperion, SIGNAL(allChannelsCleared()), this, SLOT(allChannelsCleared()));
|
||||||
|
|
||||||
|
// read all effects
|
||||||
|
const Json::Value & paths = jsonEffectConfig["paths"];
|
||||||
|
for (Json::UInt i = 0; i < paths.size(); ++i)
|
||||||
|
{
|
||||||
|
const std::string & path = paths[i].asString();
|
||||||
|
QDir directory(QString::fromStdString(path));
|
||||||
|
if (!directory.exists())
|
||||||
|
{
|
||||||
|
std::cerr << "Effect directory can not be loaded: " << path << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||||
|
foreach (const QString & filename, filenames)
|
||||||
|
{
|
||||||
|
EffectDefinition def;
|
||||||
|
if (loadEffectDefinition(path, filename.toStdString(), def))
|
||||||
|
{
|
||||||
|
_availableEffects.push_back(def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the python interpreter
|
||||||
|
std::cout << "Initializing Python interpreter" << std::endl;
|
||||||
|
Py_InitializeEx(0);
|
||||||
|
PyEval_InitThreads(); // Create the GIL
|
||||||
|
_mainThreadState = PyEval_SaveThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectEngine::~EffectEngine()
|
||||||
|
{
|
||||||
|
// clean up the Python interpreter
|
||||||
|
std::cout << "Cleaning up Python interpreter" << std::endl;
|
||||||
|
PyEval_RestoreThread(_mainThreadState);
|
||||||
|
Py_Finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::list<EffectDefinition> &EffectEngine::getEffects() const
|
||||||
|
{
|
||||||
|
return _availableEffects;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EffectEngine::loadEffectDefinition(const std::string &path, const std::string &effectConfigFile, EffectDefinition & effectDefinition)
|
||||||
|
{
|
||||||
|
std::string fileName = path + QDir::separator().toAscii() + effectConfigFile;
|
||||||
|
std::ifstream file(fileName.c_str());
|
||||||
|
|
||||||
|
if (!file.is_open())
|
||||||
|
{
|
||||||
|
std::cerr << "Effect file '" << fileName << "' could not be loaded" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the json config file
|
||||||
|
Json::Reader jsonReader;
|
||||||
|
Json::Value config;
|
||||||
|
if (!jsonReader.parse(file, config, false))
|
||||||
|
{
|
||||||
|
std::cerr << "Error while reading effect '" << fileName << "': " << jsonReader.getFormattedErrorMessages() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the json schema file
|
||||||
|
QResource schemaData(":effect-schema");
|
||||||
|
JsonSchemaChecker schemaChecker;
|
||||||
|
Json::Value schema;
|
||||||
|
Json::Reader().parse(reinterpret_cast<const char *>(schemaData.data()), reinterpret_cast<const char *>(schemaData.data()) + schemaData.size(), schema, false);
|
||||||
|
schemaChecker.setSchema(schema);
|
||||||
|
if (!schemaChecker.validate(config))
|
||||||
|
{
|
||||||
|
const std::list<std::string> & errors = schemaChecker.getMessages();
|
||||||
|
foreach (const std::string & error, errors) {
|
||||||
|
std::cerr << "Error while checking '" << fileName << "':" << error << std::endl;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup the definition
|
||||||
|
effectDefinition.name = config["name"].asString();
|
||||||
|
effectDefinition.script = path + QDir::separator().toAscii() + config["script"].asString();
|
||||||
|
effectDefinition.args = config["args"];
|
||||||
|
|
||||||
|
// return succes
|
||||||
|
std::cout << "Effect loaded: " + effectDefinition.name << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EffectEngine::runEffect(const std::string &effectName, int priority, int timeout)
|
||||||
|
{
|
||||||
|
return runEffect(effectName, Json::Value(Json::nullValue), priority, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int EffectEngine::runEffect(const std::string &effectName, const Json::Value &args, int priority, int timeout)
|
||||||
|
{
|
||||||
|
std::cout << "run effect " << effectName << " on channel " << priority << std::endl;
|
||||||
|
|
||||||
|
const EffectDefinition * effectDefinition = nullptr;
|
||||||
|
for (const EffectDefinition & e : _availableEffects)
|
||||||
|
{
|
||||||
|
if (e.name == effectName)
|
||||||
|
{
|
||||||
|
effectDefinition = &e;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (effectDefinition == nullptr)
|
||||||
|
{
|
||||||
|
// no such effect
|
||||||
|
std::cerr << "effect " << effectName << " not found" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return runEffectScript(effectDefinition->script, args.isNull() ? effectDefinition->args : args, priority, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int EffectEngine::runEffectScript(const std::string &script, const Json::Value &args, int priority, int timeout)
|
||||||
|
{
|
||||||
|
// clear current effect on the channel
|
||||||
|
channelCleared(priority);
|
||||||
|
|
||||||
|
// create the effect
|
||||||
|
Effect * effect = new Effect(priority, timeout, script, args);
|
||||||
|
connect(effect, SIGNAL(setColors(int,std::vector<ColorRgb>,int,bool)), _hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int,bool)), Qt::QueuedConnection);
|
||||||
|
connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*)));
|
||||||
|
_activeEffects.push_back(effect);
|
||||||
|
|
||||||
|
// start the effect
|
||||||
|
effect->start();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectEngine::channelCleared(int priority)
|
||||||
|
{
|
||||||
|
for (Effect * effect : _activeEffects)
|
||||||
|
{
|
||||||
|
if (effect->getPriority() == priority)
|
||||||
|
{
|
||||||
|
effect->abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectEngine::allChannelsCleared()
|
||||||
|
{
|
||||||
|
for (Effect * effect : _activeEffects)
|
||||||
|
{
|
||||||
|
effect->abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectEngine::effectFinished(Effect *effect)
|
||||||
|
{
|
||||||
|
if (!effect->isAbortRequested())
|
||||||
|
{
|
||||||
|
// effect stopped by itself. Clear the channel
|
||||||
|
_hyperion->clear(effect->getPriority());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "effect finished" << std::endl;
|
||||||
|
for (auto effectIt = _activeEffects.begin(); effectIt != _activeEffects.end(); ++effectIt)
|
||||||
|
{
|
||||||
|
if (*effectIt == effect)
|
||||||
|
{
|
||||||
|
_activeEffects.erase(effectIt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup the effect
|
||||||
|
effect->deleteLater();
|
||||||
|
}
|
5
libsrc/effectengine/EffectEngine.qrc
Normal file
5
libsrc/effectengine/EffectEngine.qrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file alias="effect-schema">EffectDefinition.schema.json</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
@ -11,15 +11,6 @@ include_directories(
|
|||||||
../../include/hidapi
|
../../include/hidapi
|
||||||
${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device
|
${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device
|
||||||
|
|
||||||
# set the build options
|
|
||||||
option (LIBUDEV_REQUIRED "Add libudev to the build (needed on Ubuntu 13.10, but gives errors when added on the RPi) :-S" OFF)
|
|
||||||
message(STATUS "LIBUDEV_REQUIRED = " ${LIBUDEV_REQUIRED})
|
|
||||||
|
|
||||||
if (LIBUDEV_REQUIRED)
|
|
||||||
find_package(UDev REQUIRED)
|
|
||||||
include_directories(${UDEV_INCLUDE_DIR})
|
|
||||||
endif (LIBUDEV_REQUIRED)
|
|
||||||
|
|
||||||
# Group the headers that go through the MOC compiler
|
# Group the headers that go through the MOC compiler
|
||||||
SET(Hyperion_QT_HEADERS
|
SET(Hyperion_QT_HEADERS
|
||||||
${CURRENT_HEADER_DIR}/Hyperion.h
|
${CURRENT_HEADER_DIR}/Hyperion.h
|
||||||
@ -49,6 +40,7 @@ SET(Hyperion_HEADERS
|
|||||||
${CURRENT_SOURCE_DIR}/device/LedDeviceLpd6803.h
|
${CURRENT_SOURCE_DIR}/device/LedDeviceLpd6803.h
|
||||||
${CURRENT_SOURCE_DIR}/device/LedDeviceLpd8806.h
|
${CURRENT_SOURCE_DIR}/device/LedDeviceLpd8806.h
|
||||||
${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.h
|
${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.h
|
||||||
|
${CURRENT_SOURCE_DIR}/device/LedDevicePaintpack.h
|
||||||
${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.h
|
${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.h
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -74,6 +66,7 @@ SET(Hyperion_SOURCES
|
|||||||
${CURRENT_SOURCE_DIR}/device/LedDeviceLpd8806.cpp
|
${CURRENT_SOURCE_DIR}/device/LedDeviceLpd8806.cpp
|
||||||
${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.cpp
|
${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.cpp
|
||||||
${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.cpp
|
${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.cpp
|
||||||
|
${CURRENT_SOURCE_DIR}/device/LedDevicePaintpack.cpp
|
||||||
${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.cpp
|
${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -95,14 +88,10 @@ add_library(hyperion
|
|||||||
|
|
||||||
target_link_libraries(hyperion
|
target_link_libraries(hyperion
|
||||||
hyperion-utils
|
hyperion-utils
|
||||||
|
effectengine
|
||||||
|
hidapi-libusb
|
||||||
serialport
|
serialport
|
||||||
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev
|
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
${QT_LIBRARIES}
|
${QT_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (LIBUDEV_REQUIRED)
|
|
||||||
target_link_libraries(hyperion
|
|
||||||
${UDEV_LIBRARIES} # apt-get install libudev-dev
|
|
||||||
)
|
|
||||||
endif (LIBUDEV_REQUIRED)
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
// QT includes
|
// QT includes
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QThread>
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
@ -22,12 +23,16 @@
|
|||||||
#include "device/LedDeviceTest.h"
|
#include "device/LedDeviceTest.h"
|
||||||
#include "device/LedDeviceWs2801.h"
|
#include "device/LedDeviceWs2801.h"
|
||||||
#include "device/LedDeviceAdalight.h"
|
#include "device/LedDeviceAdalight.h"
|
||||||
|
#include "device/LedDevicePaintpack.h"
|
||||||
#include "device/LedDeviceLightpack.h"
|
#include "device/LedDeviceLightpack.h"
|
||||||
#include "device/LedDeviceMultiLightpack.h"
|
#include "device/LedDeviceMultiLightpack.h"
|
||||||
|
|
||||||
#include "MultiColorTransform.h"
|
#include "MultiColorTransform.h"
|
||||||
#include "LinearColorSmoothing.h"
|
#include "LinearColorSmoothing.h"
|
||||||
|
|
||||||
|
// effect engine includes
|
||||||
|
#include <effectengine/EffectEngine.h>
|
||||||
|
|
||||||
LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig)
|
LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig)
|
||||||
{
|
{
|
||||||
std::cout << "Device configuration: " << deviceConfig << std::endl;
|
std::cout << "Device configuration: " << deviceConfig << std::endl;
|
||||||
@ -36,7 +41,7 @@ LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig)
|
|||||||
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
||||||
|
|
||||||
LedDevice* device = nullptr;
|
LedDevice* device = nullptr;
|
||||||
if (type == "ws2801")
|
if (type == "ws2801" || type == "lightberry")
|
||||||
{
|
{
|
||||||
const std::string output = deviceConfig["output"].asString();
|
const std::string output = deviceConfig["output"].asString();
|
||||||
const unsigned rate = deviceConfig["rate"].asInt();
|
const unsigned rate = deviceConfig["rate"].asInt();
|
||||||
@ -95,6 +100,13 @@ LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig)
|
|||||||
|
|
||||||
device = deviceLightpack;
|
device = deviceLightpack;
|
||||||
}
|
}
|
||||||
|
else if (type == "paintpack")
|
||||||
|
{
|
||||||
|
LedDevicePaintpack * devicePainLightpack = new LedDevicePaintpack();
|
||||||
|
devicePainLightpack->open();
|
||||||
|
|
||||||
|
device = devicePainLightpack;
|
||||||
|
}
|
||||||
else if (type == "multi-lightpack")
|
else if (type == "multi-lightpack")
|
||||||
{
|
{
|
||||||
LedDeviceMultiLightpack* deviceLightpack = new LedDeviceMultiLightpack();
|
LedDeviceMultiLightpack* deviceLightpack = new LedDeviceMultiLightpack();
|
||||||
@ -211,7 +223,15 @@ MultiColorTransform * Hyperion::createLedColorsTransform(const unsigned ledCnt,
|
|||||||
ColorTransform * colorTransform = createColorTransform(config);
|
ColorTransform * colorTransform = createColorTransform(config);
|
||||||
transform->addTransform(colorTransform);
|
transform->addTransform(colorTransform);
|
||||||
|
|
||||||
const QString ledIndicesStr = config.get("leds", "").asCString();
|
const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed();
|
||||||
|
if (ledIndicesStr.compare("*") == 0)
|
||||||
|
{
|
||||||
|
// Special case for indices '*' => all leds
|
||||||
|
transform->setTransformForLed(colorTransform->_id, 0, ledCnt-1);
|
||||||
|
std::cout << "ColorTransform '" << colorTransform->_id << "' => [0; "<< ledCnt-1 << "]" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!overallExp.exactMatch(ledIndicesStr))
|
if (!overallExp.exactMatch(ledIndicesStr))
|
||||||
{
|
{
|
||||||
std::cerr << "Given led indices " << i << " not correct format: " << ledIndicesStr.toStdString() << std::endl;
|
std::cerr << "Given led indices " << i << " not correct format: " << ledIndicesStr.toStdString() << std::endl;
|
||||||
@ -342,6 +362,7 @@ Hyperion::Hyperion(const Json::Value &jsonConfig) :
|
|||||||
_raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])),
|
_raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])),
|
||||||
_colorOrder(createColorOrder(jsonConfig["device"])),
|
_colorOrder(createColorOrder(jsonConfig["device"])),
|
||||||
_device(createDevice(jsonConfig["device"])),
|
_device(createDevice(jsonConfig["device"])),
|
||||||
|
_effectEngine(nullptr),
|
||||||
_timer()
|
_timer()
|
||||||
{
|
{
|
||||||
if (!_raw2ledTransform->verifyTransforms())
|
if (!_raw2ledTransform->verifyTransforms())
|
||||||
@ -358,6 +379,9 @@ Hyperion::Hyperion(const Json::Value &jsonConfig) :
|
|||||||
_timer.setSingleShot(true);
|
_timer.setSingleShot(true);
|
||||||
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
|
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
|
||||||
|
|
||||||
|
// create the effect engine
|
||||||
|
_effectEngine = new EffectEngine(this, jsonConfig["effects"]);
|
||||||
|
|
||||||
// initialize the leds
|
// initialize the leds
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
@ -369,6 +393,9 @@ Hyperion::~Hyperion()
|
|||||||
clearall();
|
clearall();
|
||||||
_device->switchOff();
|
_device->switchOff();
|
||||||
|
|
||||||
|
// delete the effect engine
|
||||||
|
delete _effectEngine;
|
||||||
|
|
||||||
// Delete the Led-String
|
// Delete the Led-String
|
||||||
delete _device;
|
delete _device;
|
||||||
|
|
||||||
@ -381,17 +408,23 @@ unsigned Hyperion::getLedCount() const
|
|||||||
return _ledString.leds().size();
|
return _ledString.leds().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_ms)
|
void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_ms, bool clearEffects)
|
||||||
{
|
{
|
||||||
// create led output
|
// create led output
|
||||||
std::vector<ColorRgb> ledColors(_ledString.leds().size(), color);
|
std::vector<ColorRgb> ledColors(_ledString.leds().size(), color);
|
||||||
|
|
||||||
// set colors
|
// set colors
|
||||||
setColors(priority, ledColors, timeout_ms);
|
setColors(priority, ledColors, timeout_ms, clearEffects);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hyperion::setColors(int priority, const std::vector<ColorRgb>& ledColors, const int timeout_ms)
|
void Hyperion::setColors(int priority, const std::vector<ColorRgb>& ledColors, const int timeout_ms, bool clearEffects)
|
||||||
{
|
{
|
||||||
|
// clear effects if this call does not come from an effect
|
||||||
|
if (clearEffects)
|
||||||
|
{
|
||||||
|
_effectEngine->channelCleared(priority);
|
||||||
|
}
|
||||||
|
|
||||||
if (timeout_ms > 0)
|
if (timeout_ms > 0)
|
||||||
{
|
{
|
||||||
const uint64_t timeoutTime = QDateTime::currentMSecsSinceEpoch() + timeout_ms;
|
const uint64_t timeoutTime = QDateTime::currentMSecsSinceEpoch() + timeout_ms;
|
||||||
@ -435,6 +468,10 @@ void Hyperion::clear(int priority)
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send clear signal to the effect engine
|
||||||
|
// (outside the check so the effect gets cleared even when the effect is not sending colors)
|
||||||
|
_effectEngine->channelCleared(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hyperion::clearall()
|
void Hyperion::clearall()
|
||||||
@ -443,6 +480,9 @@ void Hyperion::clearall()
|
|||||||
|
|
||||||
// update leds
|
// update leds
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
// send clearall signal to the effect engine
|
||||||
|
_effectEngine->allChannelsCleared();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<int> Hyperion::getActivePriorities() const
|
QList<int> Hyperion::getActivePriorities() const
|
||||||
@ -455,6 +495,21 @@ const Hyperion::InputInfo &Hyperion::getPriorityInfo(const int priority) const
|
|||||||
return _muxer.getInputInfo(priority);
|
return _muxer.getInputInfo(priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::list<EffectDefinition> & Hyperion::getEffects() const
|
||||||
|
{
|
||||||
|
return _effectEngine->getEffects();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Hyperion::setEffect(const std::string &effectName, int priority, int timeout)
|
||||||
|
{
|
||||||
|
return _effectEngine->runEffect(effectName, priority, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Hyperion::setEffect(const std::string &effectName, const Json::Value &args, int priority, int timeout)
|
||||||
|
{
|
||||||
|
return _effectEngine->runEffect(effectName, args, priority, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
void Hyperion::update()
|
void Hyperion::update()
|
||||||
{
|
{
|
||||||
// Update the muxer, cleaning obsolete priorities
|
// Update the muxer, cleaning obsolete priorities
|
||||||
|
@ -21,6 +21,11 @@ ImageProcessor::~ImageProcessor()
|
|||||||
delete _borderProcessor;
|
delete _borderProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned ImageProcessor::getLedCount() const
|
||||||
|
{
|
||||||
|
return mLedString.leds().size();
|
||||||
|
}
|
||||||
|
|
||||||
void ImageProcessor::setSize(const unsigned width, const unsigned height)
|
void ImageProcessor::setSize(const unsigned width, const unsigned height)
|
||||||
{
|
{
|
||||||
// Check if the existing buffer-image is already the correct dimensions
|
// Check if the existing buffer-image is already the correct dimensions
|
||||||
|
77
libsrc/hyperion/device/LedDevicePaintpack.cpp
Normal file
77
libsrc/hyperion/device/LedDevicePaintpack.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
// Hyperion includes
|
||||||
|
#include "LedDevicePaintpack.h"
|
||||||
|
|
||||||
|
LedDevicePaintpack::LedDevicePaintpack() :
|
||||||
|
LedDevice(),
|
||||||
|
_deviceHandle(nullptr)
|
||||||
|
{
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
int LedDevicePaintpack::open()
|
||||||
|
{
|
||||||
|
// initialize the usb context
|
||||||
|
int error = hid_init();
|
||||||
|
if (error != 0)
|
||||||
|
{
|
||||||
|
std::cerr << "Error while initializing the hidapi context" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
std::cout << "Hidapi initialized" << std::endl;
|
||||||
|
|
||||||
|
// Initialise the paintpack device
|
||||||
|
const unsigned short Paintpack_VendorId = 0x0ebf;
|
||||||
|
const unsigned short Paintpack_ProductId = 0x0025;
|
||||||
|
_deviceHandle = hid_open(Paintpack_VendorId, Paintpack_ProductId, nullptr);
|
||||||
|
if (_deviceHandle == nullptr)
|
||||||
|
{
|
||||||
|
// Failed to open the device
|
||||||
|
std::cerr << "Failed to open HID Paintpakc device " << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LedDevicePaintpack::~LedDevicePaintpack()
|
||||||
|
{
|
||||||
|
if (_deviceHandle != nullptr)
|
||||||
|
{
|
||||||
|
hid_close(_deviceHandle);
|
||||||
|
_deviceHandle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
hid_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
int LedDevicePaintpack::write(const std::vector<ColorRgb>& ledValues)
|
||||||
|
{
|
||||||
|
if (_ledBuffer.size() < 3 + ledValues.size()*3)
|
||||||
|
{
|
||||||
|
_ledBuffer.resize(3 + ledValues.size()*3, uint8_t(0));
|
||||||
|
|
||||||
|
_ledBuffer[0] = 0;
|
||||||
|
_ledBuffer[1] = 3;
|
||||||
|
_ledBuffer[2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bufIt = _ledBuffer.begin()+3;
|
||||||
|
for (const ColorRgb & ledValue : ledValues)
|
||||||
|
{
|
||||||
|
*bufIt = ledValue.red;
|
||||||
|
++bufIt;
|
||||||
|
*bufIt = ledValue.green;
|
||||||
|
++bufIt;
|
||||||
|
*bufIt = ledValue.blue;
|
||||||
|
++bufIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hid_write(_deviceHandle, _ledBuffer.data(), _ledBuffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int LedDevicePaintpack::switchOff()
|
||||||
|
{
|
||||||
|
std::fill(_ledBuffer.begin()+3, _ledBuffer.end(), uint8_t(0));
|
||||||
|
return hid_write(_deviceHandle, _ledBuffer.data(), _ledBuffer.size());
|
||||||
|
}
|
59
libsrc/hyperion/device/LedDevicePaintpack.h
Normal file
59
libsrc/hyperion/device/LedDevicePaintpack.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// STL includes
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// libusb include
|
||||||
|
#include <hidapi/hidapi.h>
|
||||||
|
|
||||||
|
// Hyperion includes
|
||||||
|
#include <hyperion/LedDevice.h>
|
||||||
|
|
||||||
|
///
|
||||||
|
/// LedDevice implementation for a paintpack device ()
|
||||||
|
///
|
||||||
|
class LedDevicePaintpack : public LedDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs the paintpack device
|
||||||
|
*/
|
||||||
|
LedDevicePaintpack();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructs the paintpack device, closes USB connection if open
|
||||||
|
*/
|
||||||
|
virtual ~LedDevicePaintpack();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the Paintpack device
|
||||||
|
*
|
||||||
|
* @return Zero on succes else negative
|
||||||
|
*/
|
||||||
|
int open();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Writes the RGB-Color values to the leds.
|
||||||
|
///
|
||||||
|
/// @param[in] ledValues The RGB-color per led
|
||||||
|
///
|
||||||
|
/// @return Zero on success else negative
|
||||||
|
///
|
||||||
|
virtual int write(const std::vector<ColorRgb>& ledValues);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Switch the leds off
|
||||||
|
///
|
||||||
|
/// @return Zero on success else negative
|
||||||
|
///
|
||||||
|
virtual int switchOff();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// libusb device handle
|
||||||
|
hid_device * _deviceHandle;
|
||||||
|
|
||||||
|
/// buffer for led data
|
||||||
|
std::vector<uint8_t> _ledBuffer;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
@ -200,6 +200,21 @@
|
|||||||
"additionalProperties" : false
|
"additionalProperties" : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"effects" :
|
||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false,
|
||||||
|
"properties" : {
|
||||||
|
"paths" : {
|
||||||
|
"type" : "array",
|
||||||
|
"required" : false,
|
||||||
|
"items" : {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
},
|
||||||
"blackborderdetector" :
|
"blackborderdetector" :
|
||||||
{
|
{
|
||||||
"type" : "object",
|
"type" : "object",
|
||||||
@ -249,12 +264,12 @@
|
|||||||
"type" : "object",
|
"type" : "object",
|
||||||
"required" : false,
|
"required" : false,
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"type" : {
|
"path" : {
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
"required" : true
|
"required" : true
|
||||||
},
|
},
|
||||||
"duration_ms" : {
|
"effect" : {
|
||||||
"type" : "integer",
|
"type" : "string",
|
||||||
"required" : true
|
"required" : true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -95,6 +95,8 @@ void JsonClientConnection::handleMessage(const std::string &messageString)
|
|||||||
handleColorCommand(message);
|
handleColorCommand(message);
|
||||||
else if (command == "image")
|
else if (command == "image")
|
||||||
handleImageCommand(message);
|
handleImageCommand(message);
|
||||||
|
else if (command == "effect")
|
||||||
|
handleEffectCommand(message);
|
||||||
else if (command == "serverinfo")
|
else if (command == "serverinfo")
|
||||||
handleServerInfoCommand(message);
|
handleServerInfoCommand(message);
|
||||||
else if (command == "clear")
|
else if (command == "clear")
|
||||||
@ -112,10 +114,33 @@ void JsonClientConnection::handleColorCommand(const Json::Value &message)
|
|||||||
// extract parameters
|
// extract parameters
|
||||||
int priority = message["priority"].asInt();
|
int priority = message["priority"].asInt();
|
||||||
int duration = message.get("duration", -1).asInt();
|
int duration = message.get("duration", -1).asInt();
|
||||||
ColorRgb color = {uint8_t(message["color"][0u].asInt()), uint8_t(message["color"][1u].asInt()), uint8_t(message["color"][2u].asInt())};
|
|
||||||
|
std::vector<ColorRgb> colorData(_hyperion->getLedCount());
|
||||||
|
const Json::Value & jsonColor = message["color"];
|
||||||
|
Json::UInt i = 0;
|
||||||
|
for (; i < jsonColor.size()/3 && i < _hyperion->getLedCount(); ++i)
|
||||||
|
{
|
||||||
|
colorData[i].red = uint8_t(message["color"][3u*i].asInt());
|
||||||
|
colorData[i].green = uint8_t(message["color"][3u*i+1u].asInt());
|
||||||
|
colorData[i].blue = uint8_t(message["color"][3u*i+2u].asInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy full blocks of led colors
|
||||||
|
unsigned size = i;
|
||||||
|
while (i + size < _hyperion->getLedCount())
|
||||||
|
{
|
||||||
|
memcpy(&(colorData[i]), colorData.data(), size * sizeof(ColorRgb));
|
||||||
|
i += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy remaining block of led colors
|
||||||
|
if (i < _hyperion->getLedCount())
|
||||||
|
{
|
||||||
|
memcpy(&(colorData[i]), colorData.data(), (_hyperion->getLedCount()-i) * sizeof(ColorRgb));
|
||||||
|
}
|
||||||
|
|
||||||
// set output
|
// set output
|
||||||
_hyperion->setColor(priority, color, duration);
|
_hyperion->setColors(priority, colorData, duration);
|
||||||
|
|
||||||
// send reply
|
// send reply
|
||||||
sendSuccessReply();
|
sendSuccessReply();
|
||||||
@ -152,7 +177,29 @@ void JsonClientConnection::handleImageCommand(const Json::Value &message)
|
|||||||
sendSuccessReply();
|
sendSuccessReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonClientConnection::handleServerInfoCommand(const Json::Value &message)
|
void JsonClientConnection::handleEffectCommand(const Json::Value &message)
|
||||||
|
{
|
||||||
|
// extract parameters
|
||||||
|
int priority = message["priority"].asInt();
|
||||||
|
int duration = message.get("duration", -1).asInt();
|
||||||
|
const Json::Value & effect = message["effect"];
|
||||||
|
const std::string & effectName = effect["name"].asString();
|
||||||
|
|
||||||
|
// set output
|
||||||
|
if (effect.isMember("args"))
|
||||||
|
{
|
||||||
|
_hyperion->setEffect(effectName, effect["args"], priority, duration);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_hyperion->setEffect(effectName, priority, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send reply
|
||||||
|
sendSuccessReply();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonClientConnection::handleServerInfoCommand(const Json::Value &)
|
||||||
{
|
{
|
||||||
// create result
|
// create result
|
||||||
Json::Value result;
|
Json::Value result;
|
||||||
@ -160,7 +207,7 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &message)
|
|||||||
Json::Value & info = result["info"];
|
Json::Value & info = result["info"];
|
||||||
|
|
||||||
// collect priority information
|
// collect priority information
|
||||||
Json::Value & priorities = info["priorities"];
|
Json::Value & priorities = info["priorities"] = Json::Value(Json::arrayValue);
|
||||||
uint64_t now = QDateTime::currentMSecsSinceEpoch();
|
uint64_t now = QDateTime::currentMSecsSinceEpoch();
|
||||||
QList<int> activePriorities = _hyperion->getActivePriorities();
|
QList<int> activePriorities = _hyperion->getActivePriorities();
|
||||||
foreach (int priority, activePriorities) {
|
foreach (int priority, activePriorities) {
|
||||||
@ -208,6 +255,19 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &message)
|
|||||||
whitelevel.append(colorTransform->_rgbBlueTransform.getWhitelevel());
|
whitelevel.append(colorTransform->_rgbBlueTransform.getWhitelevel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// collect effect info
|
||||||
|
Json::Value & effects = info["effects"] = Json::Value(Json::arrayValue);
|
||||||
|
const std::list<EffectDefinition> & effectsDefinitions = _hyperion->getEffects();
|
||||||
|
for (const EffectDefinition & effectDefinition : effectsDefinitions)
|
||||||
|
{
|
||||||
|
Json::Value effect;
|
||||||
|
effect["name"] = effectDefinition.name;
|
||||||
|
effect["script"] = effectDefinition.script;
|
||||||
|
effect["args"] = effectDefinition.args;
|
||||||
|
|
||||||
|
effects.append(effect);
|
||||||
|
}
|
||||||
|
|
||||||
// send the result
|
// send the result
|
||||||
sendMessage(result);
|
sendMessage(result);
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,13 @@ private:
|
|||||||
///
|
///
|
||||||
void handleImageCommand(const Json::Value & message);
|
void handleImageCommand(const Json::Value & message);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Handle an incoming JSON Effect message
|
||||||
|
///
|
||||||
|
/// @param message the incoming message
|
||||||
|
///
|
||||||
|
void handleEffectCommand(const Json::Value & message);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Handle an incoming JSON Server info message
|
/// Handle an incoming JSON Server info message
|
||||||
///
|
///
|
||||||
|
@ -7,5 +7,6 @@
|
|||||||
<file alias="schema-clear">schema/schema-clear.json</file>
|
<file alias="schema-clear">schema/schema-clear.json</file>
|
||||||
<file alias="schema-clearall">schema/schema-clearall.json</file>
|
<file alias="schema-clearall">schema/schema-clearall.json</file>
|
||||||
<file alias="schema-transform">schema/schema-transform.json</file>
|
<file alias="schema-transform">schema/schema-transform.json</file>
|
||||||
|
<file alias="schema-effect">schema/schema-effect.json</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -21,8 +21,7 @@
|
|||||||
"items" :{
|
"items" :{
|
||||||
"type" : "integer"
|
"type" : "integer"
|
||||||
},
|
},
|
||||||
"minItems": 3,
|
"minItems": 3
|
||||||
"maxItems": 3
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
35
libsrc/jsonserver/schema/schema-effect.json
Normal file
35
libsrc/jsonserver/schema/schema-effect.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"type":"object",
|
||||||
|
"required":true,
|
||||||
|
"properties":{
|
||||||
|
"command": {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true,
|
||||||
|
"enum" : ["effect"]
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"type": "integer",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"duration": {
|
||||||
|
"type": "integer",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"effect": {
|
||||||
|
"type": "object",
|
||||||
|
"required": true,
|
||||||
|
"properties" :{
|
||||||
|
"name" : {
|
||||||
|
"type" : "string",
|
||||||
|
"required" : true
|
||||||
|
},
|
||||||
|
"args" : {
|
||||||
|
"type" : "object",
|
||||||
|
"required" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
"command": {
|
"command": {
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
"required" : true,
|
"required" : true,
|
||||||
"enum" : ["color", "image", "serverinfo", "clear", "clearall", "transform"]
|
"enum" : ["color", "image", "effect", "serverinfo", "clear", "clearall", "transform"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,8 +168,8 @@ void JsonSchemaChecker::checkType(const Json::Value & value, const Json::Value &
|
|||||||
wrongType = !value.isString();
|
wrongType = !value.isString();
|
||||||
else if (type == "any")
|
else if (type == "any")
|
||||||
wrongType = false;
|
wrongType = false;
|
||||||
else
|
// else
|
||||||
assert(false);
|
// assert(false);
|
||||||
|
|
||||||
if (wrongType)
|
if (wrongType)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.hyperion.hypercon;
|
package org.hyperion.hypercon;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -12,9 +13,19 @@ import java.lang.reflect.ParameterizedType;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for supporting the serialisation and deserialisation of HyperCon settings.
|
||||||
|
*/
|
||||||
public class ConfigurationFile {
|
public class ConfigurationFile {
|
||||||
|
|
||||||
|
/** Temporary storage of the HyperCon configuration */
|
||||||
private final Properties mProps = new Properties();
|
private final Properties mProps = new Properties();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the configuration of HyperCon from the given filename into this {@link ConfigurationFile}
|
||||||
|
*
|
||||||
|
* @param pFilename The absolute filename containing the configuration
|
||||||
|
*/
|
||||||
public void load(String pFilename) {
|
public void load(String pFilename) {
|
||||||
mProps.clear();
|
mProps.clear();
|
||||||
// try (InputStream in = new InflaterInputStream(new FileInputStream(pFilename))){
|
// try (InputStream in = new InflaterInputStream(new FileInputStream(pFilename))){
|
||||||
@ -27,6 +38,11 @@ public class ConfigurationFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the configuration of this {@link ConfigurationFile} to the given filename
|
||||||
|
*
|
||||||
|
* @param pFilename The absolute filename to which to save the HyperCon configuration
|
||||||
|
*/
|
||||||
public void save(String pFilename) {
|
public void save(String pFilename) {
|
||||||
// try (OutputStream out = new DeflaterOutputStream(new FileOutputStream(pFilename))) {
|
// try (OutputStream out = new DeflaterOutputStream(new FileOutputStream(pFilename))) {
|
||||||
// try (OutputStream out = new GZIPOutputStream(new FileOutputStream(pFilename))) {
|
// try (OutputStream out = new GZIPOutputStream(new FileOutputStream(pFilename))) {
|
||||||
@ -37,9 +53,22 @@ public class ConfigurationFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the given object to the local properties object
|
||||||
|
*
|
||||||
|
* @param pObj The object to store
|
||||||
|
*/
|
||||||
public void store(Object pObj) {
|
public void store(Object pObj) {
|
||||||
store(pObj, pObj.getClass().getSimpleName(), "");
|
store(pObj, pObj.getClass().getSimpleName(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the given object to the local properties object (with given preamble and postamble)
|
||||||
|
*
|
||||||
|
* @param pObj The object to store
|
||||||
|
* @param preamble The preamble prepended to the key of the object members
|
||||||
|
* @param postamble The postamble appended to the key of the object members
|
||||||
|
*/
|
||||||
public void store(Object pObj, String preamble, String postamble) {
|
public void store(Object pObj, String preamble, String postamble) {
|
||||||
String className = pObj.getClass().getSimpleName();
|
String className = pObj.getClass().getSimpleName();
|
||||||
// Retrieve the member variables
|
// Retrieve the member variables
|
||||||
@ -55,37 +84,80 @@ public class ConfigurationFile {
|
|||||||
try {
|
try {
|
||||||
Object value = field.get(pObj);
|
Object value = field.get(pObj);
|
||||||
|
|
||||||
if (value.getClass().isEnum()) {
|
if (field.getType() == boolean.class) {
|
||||||
|
mProps.setProperty(key, Boolean.toString((boolean) value));
|
||||||
|
} else if (field.getType() == int.class) {
|
||||||
|
mProps.setProperty(key, Integer.toString((int) value));
|
||||||
|
} else if (field.getType() == double.class) {
|
||||||
|
mProps.setProperty(key, Double.toString((double) value));
|
||||||
|
} else if (field.getType() == String.class) {
|
||||||
|
mProps.setProperty(key, (String)value);
|
||||||
|
} else if (field.getType() == Color.class) {
|
||||||
|
Color color = (Color)value;
|
||||||
|
mProps.setProperty(key, String.format("[%d; %d; %d]", color.getRed(), color.getGreen(), color.getBlue()));
|
||||||
|
} else if (value.getClass().isEnum()) {
|
||||||
mProps.setProperty(key, ((Enum<?>)value).name());
|
mProps.setProperty(key, ((Enum<?>)value).name());
|
||||||
} else if (value.getClass().isAssignableFrom(Vector.class)) {
|
} else if (value instanceof Vector) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Vector<Object> v = (Vector<Object>) value;
|
Vector<Object> v = (Vector<Object>) value;
|
||||||
for (int i=0; i<v.size(); ++i) {
|
for (int i=0; i<v.size(); ++i) {
|
||||||
store(v.get(i), key + "[" + i + "]", "");
|
store(v.get(i), key + "[" + i + "]", "");
|
||||||
}
|
}
|
||||||
|
} else if (field.getType() == Object.class) {
|
||||||
|
if (value instanceof Boolean) {
|
||||||
|
mProps.setProperty(key, Boolean.toString((boolean) value));
|
||||||
|
} if (value instanceof Integer) {
|
||||||
|
mProps.setProperty(key, Integer.toString((int) value));
|
||||||
|
} else if (value instanceof Double) {
|
||||||
|
mProps.setProperty(key, Double.toString((double) value));
|
||||||
|
} else if (value instanceof Color) {
|
||||||
|
Color color = (Color)value;
|
||||||
|
mProps.setProperty(key, String.format("[%d; %d; %d]", color.getRed(), color.getGreen(), color.getBlue()));
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
mProps.setProperty(key, '"' + (String)value + '"');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
System.out.println("Might not be able to load: " + key + " = " + value.toString());
|
||||||
mProps.setProperty(key, value.toString());
|
mProps.setProperty(key, value.toString());
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {}
|
} catch (Throwable t) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores the object from the local properties object
|
||||||
|
*
|
||||||
|
* @param pObj The object to restore
|
||||||
|
*/
|
||||||
public void restore(Object pObj) {
|
public void restore(Object pObj) {
|
||||||
restore(pObj, mProps);
|
restore(pObj, mProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores the object from the given object object
|
||||||
|
*
|
||||||
|
* @param pObj The object to restore
|
||||||
|
* @param pProps The properties containing values for the members of obj
|
||||||
|
*/
|
||||||
public void restore(Object pObj, Properties pProps) {
|
public void restore(Object pObj, Properties pProps) {
|
||||||
String className = pObj.getClass().getSimpleName();
|
String className = pObj.getClass().getSimpleName();
|
||||||
restore(pObj, pProps, className + ".");
|
restore(pObj, pProps, className + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores the object from the given settings object, using the given preamble
|
||||||
|
*
|
||||||
|
* @param pObj The object to restore
|
||||||
|
* @param pProps The properties containing values for the members of obj
|
||||||
|
* @param pPreamble The preamble to use
|
||||||
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void restore(Object pObj, Properties pProps, String pPreamble) {
|
public void restore(Object pObj, Properties pProps, String pPreamble) {
|
||||||
// Retrieve the member variables
|
// Retrieve the member variables
|
||||||
Field[] fields = pObj.getClass().getDeclaredFields();
|
Field[] fields = pObj.getClass().getDeclaredFields();
|
||||||
// Iterate each variable
|
// Iterate each variable
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
if (field.getType().isAssignableFrom(Vector.class)) {
|
if (field.getType().equals(Vector.class)) {
|
||||||
// Obtain the Vector
|
// Obtain the Vector
|
||||||
Vector<Object> vector;
|
Vector<Object> vector;
|
||||||
try {
|
try {
|
||||||
@ -157,19 +229,56 @@ public class ConfigurationFile {
|
|||||||
field.set(pObj, Integer.parseInt(value));
|
field.set(pObj, Integer.parseInt(value));
|
||||||
} else if (field.getType() == double.class) {
|
} else if (field.getType() == double.class) {
|
||||||
field.set(pObj, Double.parseDouble(value));
|
field.set(pObj, Double.parseDouble(value));
|
||||||
|
} else if (field.getType() == Color.class) {
|
||||||
|
String[] channelValues = value.substring(1, value.length()-1).split(";");
|
||||||
|
field.set(pObj, new Color(Integer.parseInt(channelValues[0].trim()), Integer.parseInt(channelValues[1].trim()), Integer.parseInt(channelValues[2].trim())));
|
||||||
|
} else if (field.getType() == String.class) {
|
||||||
|
field.set(pObj, value);
|
||||||
} else if (field.getType().isEnum()) {
|
} else if (field.getType().isEnum()) {
|
||||||
Method valMet = field.getType().getMethod("valueOf", String.class);
|
Method valMet = field.getType().getMethod("valueOf", String.class);
|
||||||
Object enumVal = valMet.invoke(null, value);
|
Object enumVal = valMet.invoke(null, value);
|
||||||
field.set(pObj, enumVal);
|
field.set(pObj, enumVal);
|
||||||
|
} else if (field.getType() == Object.class) {
|
||||||
|
// We can not infer from the type of the field, let's try the actual stored value
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
// We will never known ...
|
||||||
|
} else if (value.startsWith("[") && value.endsWith("]")) {
|
||||||
|
String[] channelValues = value.substring(1, value.length()-1).split(";");
|
||||||
|
field.set(pObj, new Color(Integer.parseInt(channelValues[0].trim()), Integer.parseInt(channelValues[1].trim()), Integer.parseInt(channelValues[2].trim())));
|
||||||
|
} else if (value.startsWith("\"") && value.endsWith("\"")) {
|
||||||
|
field.set(pObj, value.substring(1, value.length()-1));
|
||||||
} else {
|
} else {
|
||||||
field.set(pObj, value);
|
try {
|
||||||
|
int i = Integer.parseInt(value);
|
||||||
|
field.set(pObj, i);
|
||||||
|
} catch (Throwable t1) {
|
||||||
|
try {
|
||||||
|
double d = Double.parseDouble(value);
|
||||||
|
field.set(pObj, d);
|
||||||
|
} catch (Throwable t2) {
|
||||||
|
try {
|
||||||
|
boolean bool = Boolean.parseBoolean(value);
|
||||||
|
field.set(pObj, bool);
|
||||||
|
} catch (Throwable t3) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
System.out.println("Failed to parse value(" + value + ") for " + key);
|
System.out.println("Failed to parse value(" + value + ") for " + key);
|
||||||
|
t.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a String representation of this ConfigurationFile, which is the {@link #toString()}
|
||||||
|
* of the underlying {@link Properties}
|
||||||
|
*
|
||||||
|
* @return The String representation of this ConfigurationFile
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mProps.toString();
|
return mProps.toString();
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package org.hyperion.hypercon;
|
||||||
|
|
||||||
|
public class HyperConConfig {
|
||||||
|
|
||||||
|
public boolean loadDefaultEffect = true;
|
||||||
|
}
|
@ -0,0 +1,226 @@
|
|||||||
|
package org.hyperion.hypercon;
|
||||||
|
|
||||||
|
public class JsonStringBuffer {
|
||||||
|
|
||||||
|
private final StringBuffer mStrBuf = new StringBuffer();
|
||||||
|
|
||||||
|
private final int mStartIndentLevel;
|
||||||
|
private int mIndentLevel = 0;
|
||||||
|
|
||||||
|
/** Flag indicating that the parts written are 'commented-out' */
|
||||||
|
private boolean mComment = false;
|
||||||
|
|
||||||
|
public JsonStringBuffer() {
|
||||||
|
this(0);
|
||||||
|
|
||||||
|
mStrBuf.append("{\n");
|
||||||
|
++mIndentLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonStringBuffer(int pIndentLevel) {
|
||||||
|
mStartIndentLevel = pIndentLevel;
|
||||||
|
mIndentLevel = pIndentLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void newLine() {
|
||||||
|
mStrBuf.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finish() {
|
||||||
|
|
||||||
|
for (int i=0; i<mIndentLevel; ++i) {
|
||||||
|
mStrBuf.append('\t');
|
||||||
|
}
|
||||||
|
mStrBuf.append("\"end-of-json\" : \"end-of-json\"\n");
|
||||||
|
|
||||||
|
--mIndentLevel;
|
||||||
|
if (mIndentLevel != mStartIndentLevel) {
|
||||||
|
System.err.println("Json write closed in incorrect state!");
|
||||||
|
}
|
||||||
|
for (int i=0; i<mIndentLevel; ++i) {
|
||||||
|
mStrBuf.append('\t');
|
||||||
|
}
|
||||||
|
mStrBuf.append("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeComment(String pComment) {
|
||||||
|
String[] commentLines = pComment.split("\\r?\\n");
|
||||||
|
|
||||||
|
for (String commentLine : commentLines) {
|
||||||
|
for (int i=0; i<mIndentLevel; ++i) {
|
||||||
|
mStrBuf.append('\t');
|
||||||
|
}
|
||||||
|
mStrBuf.append("/// ").append(commentLine).append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleComment(boolean b) {
|
||||||
|
mComment = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startLine() {
|
||||||
|
if (mComment) mStrBuf.append("// ");
|
||||||
|
for (int i=0; i<mIndentLevel; ++i) {
|
||||||
|
mStrBuf.append('\t');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startObject(String pKey) {
|
||||||
|
if (!pKey.isEmpty()) {
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append('"').append(pKey).append('"').append(" : \n");
|
||||||
|
}
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append("{\n");
|
||||||
|
|
||||||
|
++mIndentLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopObject(boolean endOfSection) {
|
||||||
|
--mIndentLevel;
|
||||||
|
|
||||||
|
startLine();
|
||||||
|
if (endOfSection) {
|
||||||
|
mStrBuf.append("}\n");
|
||||||
|
} else {
|
||||||
|
mStrBuf.append("},\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopObject() {
|
||||||
|
--mIndentLevel;
|
||||||
|
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append("},\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startArray(String pKey) {
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append('"').append(pKey).append('"').append(" : \n");
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append("[\n");
|
||||||
|
|
||||||
|
++mIndentLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopArray(boolean lastValue) {
|
||||||
|
--mIndentLevel;
|
||||||
|
|
||||||
|
startLine();
|
||||||
|
if (lastValue) {
|
||||||
|
mStrBuf.append("]\n");
|
||||||
|
} else {
|
||||||
|
mStrBuf.append("],\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addRawValue(String pKey, String pValue, boolean lastValue) {
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append('"').append(pKey).append('"').append(" : ").append(pValue);
|
||||||
|
if (lastValue) {
|
||||||
|
mStrBuf.append("\n");
|
||||||
|
} else {
|
||||||
|
mStrBuf.append(",\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addValue(String pKey, String pValue, boolean lastValue) {
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append('"').append(pKey).append('"').append(" : ").append('"').append(pValue).append('"');
|
||||||
|
if (lastValue) {
|
||||||
|
mStrBuf.append("\n");
|
||||||
|
} else {
|
||||||
|
mStrBuf.append(",\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addValue(String pKey, double pValue, boolean lastValue) {
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append('"').append(pKey).append('"').append(" : ").append(pValue);
|
||||||
|
if (lastValue) {
|
||||||
|
mStrBuf.append("\n");
|
||||||
|
} else {
|
||||||
|
mStrBuf.append(",\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addValue(String pKey, int pValue, boolean lastValue) {
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append('"').append(pKey).append('"').append(" : ").append(pValue);
|
||||||
|
if (lastValue) {
|
||||||
|
mStrBuf.append("\n");
|
||||||
|
} else {
|
||||||
|
mStrBuf.append(",\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addValue(String pKey, boolean pValue, boolean lastValue) {
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append('"').append(pKey).append('"').append(" : ").append(pValue);
|
||||||
|
if (lastValue) {
|
||||||
|
mStrBuf.append("\n");
|
||||||
|
} else {
|
||||||
|
mStrBuf.append(",\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an array element to an opened array.
|
||||||
|
*
|
||||||
|
* @param pValue The value of the element
|
||||||
|
* @param pLastValue Indicates that it is the last element in the array
|
||||||
|
*/
|
||||||
|
public void addArrayElement(String pValue, boolean pLastValue) {
|
||||||
|
startLine();
|
||||||
|
mStrBuf.append('"').append(pValue).append('"');
|
||||||
|
if (pLastValue) {
|
||||||
|
mStrBuf.append("\n");
|
||||||
|
} else {
|
||||||
|
mStrBuf.append(",\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return mStrBuf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] pArgs) {
|
||||||
|
JsonStringBuffer jsonBuf = new JsonStringBuffer();
|
||||||
|
|
||||||
|
String comment = "Device configuration contains the following fields: \n" +
|
||||||
|
"* 'name' : The user friendly name of the device (only used for display purposes) \n" +
|
||||||
|
"* 'type' : The type of the device or leds (known types for now are 'ws2801', 'lpd6803', 'sedu', 'test' and 'none') \n" +
|
||||||
|
"* 'output' : The output specification depends on selected device \n" +
|
||||||
|
" - 'ws2801' this is the device (eg '/dev/spidev0.0 or /dev/ttyS0') \n" +
|
||||||
|
" - 'test' this is the file used to write test output (eg '/home/pi/hyperion.out') \n" +
|
||||||
|
"* 'rate' : The baudrate of the output to the device \n" +
|
||||||
|
"* 'colorOrder' : The order of the color bytes ('rgb', 'rbg', 'bgr', etc.). \n";
|
||||||
|
jsonBuf.writeComment(comment);
|
||||||
|
|
||||||
|
jsonBuf.startObject("device");
|
||||||
|
jsonBuf.addValue("name", "MyPi", false);
|
||||||
|
jsonBuf.addValue("type", "ws2801", false);
|
||||||
|
jsonBuf.addValue("output", "/dev/spidev0.0", false);
|
||||||
|
jsonBuf.addValue("rate", 1000000, false);
|
||||||
|
jsonBuf.addValue("colorOrder", "rgb", true);
|
||||||
|
jsonBuf.stopObject();
|
||||||
|
|
||||||
|
jsonBuf.toggleComment(true);
|
||||||
|
jsonBuf.startObject("device");
|
||||||
|
jsonBuf.addValue("name", "MyPi", false);
|
||||||
|
jsonBuf.addValue("type", "ws2801", false);
|
||||||
|
jsonBuf.addValue("output", "/dev/spidev0.0", false);
|
||||||
|
jsonBuf.addValue("rate", 1000000, false);
|
||||||
|
jsonBuf.addValue("colorOrder", "rgb", true);
|
||||||
|
jsonBuf.stopObject();
|
||||||
|
jsonBuf.toggleComment(false);
|
||||||
|
|
||||||
|
jsonBuf.finish();
|
||||||
|
|
||||||
|
System.out.println(jsonBuf.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,21 +15,20 @@ import org.hyperion.hypercon.spec.MiscConfig;
|
|||||||
* The full configuration of Hyperion with sub-items for device, color and miscelanuous items.
|
* The full configuration of Hyperion with sub-items for device, color and miscelanuous items.
|
||||||
*/
|
*/
|
||||||
public class LedString {
|
public class LedString {
|
||||||
|
|
||||||
/** The configuration of the output device */
|
/** The configuration of the output device */
|
||||||
public DeviceConfig mDeviceConfig = new DeviceConfig();
|
public final DeviceConfig mDeviceConfig = new DeviceConfig();
|
||||||
|
|
||||||
/** THe configuration of the 'physical' led frame */
|
/** THe configuration of the 'physical' led frame */
|
||||||
public LedFrameConstruction mLedFrameConfig = new LedFrameConstruction();
|
public final LedFrameConstruction mLedFrameConfig = new LedFrameConstruction();
|
||||||
|
|
||||||
/** The configuration of the image processing */
|
/** The configuration of the image processing */
|
||||||
public ImageProcessConfig mProcessConfig = new ImageProcessConfig();
|
public final ImageProcessConfig mProcessConfig = new ImageProcessConfig();
|
||||||
|
|
||||||
/** The color adjustment configuration */
|
/** The color adjustment configuration */
|
||||||
public ColorConfig mColorConfig = new ColorConfig();
|
public final ColorConfig mColorConfig = new ColorConfig();
|
||||||
|
|
||||||
/** The miscellaneous configuration (bootsequence, blackborder detector, etc) */
|
/** The miscellaneous configuration (bootsequence, blackborder detector, etc) */
|
||||||
public MiscConfig mMiscConfig = new MiscConfig();
|
public final MiscConfig mMiscConfig = new MiscConfig();
|
||||||
|
|
||||||
/** The translation of the led frame construction and image processing to individual led configuration */
|
/** The translation of the led frame construction and image processing to individual led configuration */
|
||||||
public Vector<Led> leds;
|
public Vector<Led> leds;
|
||||||
@ -55,14 +54,23 @@ public class LedString {
|
|||||||
String colorJson = mColorConfig.toJsonString();
|
String colorJson = mColorConfig.toJsonString();
|
||||||
fw.write(colorJson + ",\n\n");
|
fw.write(colorJson + ",\n\n");
|
||||||
|
|
||||||
String ledJson = ledToJsonString();
|
JsonStringBuffer jsonBuf = new JsonStringBuffer(1);
|
||||||
fw.write(ledJson + ",\n\n");
|
|
||||||
|
|
||||||
String blackBorderJson = mProcessConfig.getBlackborderJson();
|
ledsAppendTo(jsonBuf);
|
||||||
fw.write(blackBorderJson + ",\n\n");
|
|
||||||
|
|
||||||
String miscJson = mMiscConfig.toJsonString();
|
jsonBuf.newLine();
|
||||||
fw.write(miscJson + "\n");
|
|
||||||
|
mProcessConfig.appendTo(jsonBuf);
|
||||||
|
|
||||||
|
jsonBuf.newLine();
|
||||||
|
|
||||||
|
mMiscConfig.appendTo(jsonBuf);
|
||||||
|
|
||||||
|
jsonBuf.newLine();
|
||||||
|
|
||||||
|
jsonBuf.addValue("endOfJson", "endOfJson", true);
|
||||||
|
|
||||||
|
fw.write(jsonBuf.toString());
|
||||||
|
|
||||||
fw.write("}\n");
|
fw.write("}\n");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -70,42 +78,28 @@ public class LedString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void ledsAppendTo(JsonStringBuffer pJsonBuf) {
|
||||||
* Converts the list with leds specifications to a JSON string as used by the Hyperion Deamon
|
String ledComment =
|
||||||
*
|
" The configuration for each individual led. This contains the specification of the area \n" +
|
||||||
* @return The JSON string with led-specifications
|
" averaged of an input image for each led to determine its color. Each item in the list \n" +
|
||||||
*/
|
" contains the following fields:\n" +
|
||||||
String ledToJsonString() {
|
" * index: The index of the led. This determines its location in the string of leds; zero \n" +
|
||||||
StringBuffer strBuf = new StringBuffer();
|
" being the first led.\n" +
|
||||||
|
" * hscan: The fractional part of the image along the horizontal used for the averaging \n" +
|
||||||
strBuf.append("\t/// The configuration for each individual led. This contains the specification of the area \n");
|
" (minimum and maximum inclusive)\n" +
|
||||||
strBuf.append("\t/// averaged of an input image for each led to determine its color. Each item in the list \n");
|
" * vscan: The fractional part of the image along the vertical used for the averaging \n" +
|
||||||
strBuf.append("\t/// contains the following fields:\n");
|
" (minimum and maximum inclusive)\n";
|
||||||
strBuf.append("\t/// * index: The index of the led. This determines its location in the string of leds; zero \n");
|
pJsonBuf.writeComment(ledComment);
|
||||||
strBuf.append("\t/// being the first led.\n");
|
|
||||||
strBuf.append("\t/// * hscan: The fractional part of the image along the horizontal used for the averaging \n");
|
|
||||||
strBuf.append("\t/// (minimum and maximum inclusive)\n");
|
|
||||||
strBuf.append("\t/// * vscan: The fractional part of the image along the vertical used for the averaging \n");
|
|
||||||
strBuf.append("\t/// (minimum and maximum inclusive)\n");
|
|
||||||
|
|
||||||
strBuf.append("\t\"leds\" : \n");
|
|
||||||
strBuf.append("\t[\n");
|
|
||||||
|
|
||||||
|
pJsonBuf.startArray("leds");
|
||||||
for (Led led : leds)
|
for (Led led : leds)
|
||||||
{
|
{
|
||||||
strBuf.append("\t\t{\n");
|
pJsonBuf.startObject("");
|
||||||
strBuf.append(String.format(Locale.ROOT, "\t\t\t\"index\" : %d,\n", led.mLedSeqNr));
|
pJsonBuf.addValue("index", led.mLedSeqNr, false);
|
||||||
strBuf.append(String.format(Locale.ROOT, "\t\t\t\"hscan\" : { \"minimum\" : %.4f, \"maximum\" : %.4f },\n", led.mImageRectangle.getMinX(), led.mImageRectangle.getMaxX()));
|
pJsonBuf.addRawValue("hscan", String.format(Locale.ENGLISH, "{ %1$cminimum%1$c : %2$.4f, %1$cmaximum%1$c : %3$.4f }", '"', led.mImageRectangle.getMinX(), led.mImageRectangle.getMaxX()), false);
|
||||||
strBuf.append(String.format(Locale.ROOT, "\t\t\t\"vscan\" : { \"minimum\" : %.4f, \"maximum\" : %.4f }\n", led.mImageRectangle.getMinY(), led.mImageRectangle.getMaxY()));
|
pJsonBuf.addRawValue("vscan", String.format(Locale.ENGLISH, "{ %1$cminimum%1$c : %2$.4f, %1$cmaximum%1$c : %3$.4f }", '"', led.mImageRectangle.getMinY(), led.mImageRectangle.getMaxY()), true);
|
||||||
if (led != leds.lastElement()) {
|
pJsonBuf.stopObject(led.equals(leds.get(leds.size()-1)));
|
||||||
strBuf.append("\t\t},\n");
|
|
||||||
} else {
|
|
||||||
strBuf.append("\t\t}\n");
|
|
||||||
}
|
}
|
||||||
}
|
pJsonBuf.stopArray(false);
|
||||||
|
|
||||||
strBuf.append("\t]");
|
|
||||||
|
|
||||||
return strBuf.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,9 @@ import org.hyperion.hypercon.gui.ConfigPanel;
|
|||||||
public class Main {
|
public class Main {
|
||||||
public static final String configFilename = "hypercon.dat";
|
public static final String configFilename = "hypercon.dat";
|
||||||
|
|
||||||
|
/** Some application settings (for easy/dirty access) */
|
||||||
|
public static final HyperConConfig HyperConConfig = new HyperConConfig();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point to start HyperCon
|
* Entry point to start HyperCon
|
||||||
*
|
*
|
||||||
@ -42,6 +45,7 @@ public class Main {
|
|||||||
@Override
|
@Override
|
||||||
public void windowClosing(WindowEvent e) {
|
public void windowClosing(WindowEvent e) {
|
||||||
ConfigurationFile configFile = new ConfigurationFile();
|
ConfigurationFile configFile = new ConfigurationFile();
|
||||||
|
configFile.store(Main.HyperConConfig);
|
||||||
configFile.store(ledString.mDeviceConfig);
|
configFile.store(ledString.mDeviceConfig);
|
||||||
configFile.store(ledString.mLedFrameConfig);
|
configFile.store(ledString.mLedFrameConfig);
|
||||||
configFile.store(ledString.mProcessConfig);
|
configFile.store(ledString.mProcessConfig);
|
||||||
@ -54,6 +58,7 @@ public class Main {
|
|||||||
if (new File(configFilename).exists()) {
|
if (new File(configFilename).exists()) {
|
||||||
ConfigurationFile configFile = new ConfigurationFile();
|
ConfigurationFile configFile = new ConfigurationFile();
|
||||||
configFile.load(configFilename);
|
configFile.load(configFilename);
|
||||||
|
configFile.restore(Main.HyperConConfig);
|
||||||
configFile.restore(ledString.mDeviceConfig);
|
configFile.restore(ledString.mDeviceConfig);
|
||||||
configFile.restore(ledString.mLedFrameConfig);
|
configFile.restore(ledString.mLedFrameConfig);
|
||||||
configFile.restore(ledString.mProcessConfig);
|
configFile.restore(ledString.mProcessConfig);
|
||||||
|
@ -1,126 +0,0 @@
|
|||||||
package org.hyperion.hypercon.gui;
|
|
||||||
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.beans.Transient;
|
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.GroupLayout;
|
|
||||||
import javax.swing.JCheckBox;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JSpinner;
|
|
||||||
import javax.swing.SpinnerNumberModel;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
|
|
||||||
import org.hyperion.hypercon.spec.BootSequence;
|
|
||||||
import org.hyperion.hypercon.spec.MiscConfig;
|
|
||||||
|
|
||||||
public class BootSequencePanel extends JPanel {
|
|
||||||
|
|
||||||
private final MiscConfig mMiscConfig;
|
|
||||||
|
|
||||||
private JCheckBox mBootSequenceCheck;
|
|
||||||
private JLabel mBootSequenceLabel;
|
|
||||||
private JComboBox<BootSequence> mBootSequenceCombo;
|
|
||||||
private JLabel mBootSequenceLengthLabel;
|
|
||||||
private JSpinner mBootSequenceLengthSpinner;
|
|
||||||
|
|
||||||
public BootSequencePanel(final MiscConfig pMiscconfig) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
mMiscConfig = pMiscconfig;
|
|
||||||
|
|
||||||
initialise();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transient
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
Dimension maxSize = super.getMaximumSize();
|
|
||||||
Dimension prefSize = super.getPreferredSize();
|
|
||||||
return new Dimension(maxSize.width, prefSize.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialise() {
|
|
||||||
setBorder(BorderFactory.createTitledBorder("Boot Sequence"));
|
|
||||||
|
|
||||||
mBootSequenceCheck = new JCheckBox("Enabled");
|
|
||||||
mBootSequenceCheck.setSelected(mMiscConfig.mBootsequenceEnabled);
|
|
||||||
mBootSequenceCheck.addActionListener(mActionListener);
|
|
||||||
add(mBootSequenceCheck);
|
|
||||||
|
|
||||||
mBootSequenceLabel = new JLabel("Type:");
|
|
||||||
mBootSequenceLabel.setEnabled(mMiscConfig.mBootsequenceEnabled);
|
|
||||||
add(mBootSequenceLabel);
|
|
||||||
|
|
||||||
mBootSequenceCombo = new JComboBox<>(BootSequence.values());
|
|
||||||
mBootSequenceCombo.setSelectedItem(mMiscConfig.mBootSequence);
|
|
||||||
mBootSequenceCombo.setToolTipText("The sequence used on startup to verify proper working of all the leds");
|
|
||||||
mBootSequenceCombo.addActionListener(mActionListener);
|
|
||||||
add(mBootSequenceCombo);
|
|
||||||
|
|
||||||
mBootSequenceLengthLabel = new JLabel("Length [ms]");
|
|
||||||
add(mBootSequenceLengthLabel);
|
|
||||||
|
|
||||||
mBootSequenceLengthSpinner = new JSpinner(new SpinnerNumberModel(mMiscConfig.mBootSequenceLength_ms, 500, 3600000, 1000));
|
|
||||||
mBootSequenceLengthSpinner.addChangeListener(mChangeListener);
|
|
||||||
add(mBootSequenceLengthSpinner);
|
|
||||||
|
|
||||||
|
|
||||||
GroupLayout layout = new GroupLayout(this);
|
|
||||||
layout.setAutoCreateGaps(true);
|
|
||||||
setLayout(layout);
|
|
||||||
|
|
||||||
layout.setHorizontalGroup(layout.createSequentialGroup()
|
|
||||||
.addGroup(layout.createParallelGroup()
|
|
||||||
.addComponent(mBootSequenceCheck)
|
|
||||||
.addComponent(mBootSequenceLabel)
|
|
||||||
.addComponent(mBootSequenceLengthLabel)
|
|
||||||
)
|
|
||||||
.addGroup(layout.createParallelGroup()
|
|
||||||
.addComponent(mBootSequenceCheck)
|
|
||||||
.addComponent(mBootSequenceCombo)
|
|
||||||
.addComponent(mBootSequenceLengthSpinner)
|
|
||||||
));
|
|
||||||
layout.setVerticalGroup(layout.createSequentialGroup()
|
|
||||||
.addComponent(mBootSequenceCheck)
|
|
||||||
.addGroup(layout.createParallelGroup()
|
|
||||||
.addComponent(mBootSequenceLabel)
|
|
||||||
.addComponent(mBootSequenceCombo)
|
|
||||||
)
|
|
||||||
.addGroup(layout.createParallelGroup()
|
|
||||||
.addComponent(mBootSequenceLengthLabel)
|
|
||||||
.addComponent(mBootSequenceLengthSpinner)
|
|
||||||
));
|
|
||||||
|
|
||||||
toggleEnabled(mMiscConfig.mBootsequenceEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleEnabled(boolean pEnabled) {
|
|
||||||
mBootSequenceLabel.setEnabled(pEnabled);
|
|
||||||
mBootSequenceCombo.setEnabled(pEnabled);
|
|
||||||
mBootSequenceLengthLabel.setEnabled(pEnabled);
|
|
||||||
mBootSequenceLengthSpinner.setEnabled(pEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ActionListener mActionListener = new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
mMiscConfig.mBootsequenceEnabled = mBootSequenceCheck.isSelected();
|
|
||||||
mMiscConfig.mBootSequence = (BootSequence) mBootSequenceCombo.getSelectedItem();
|
|
||||||
|
|
||||||
toggleEnabled(mMiscConfig.mBootsequenceEnabled);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final ChangeListener mChangeListener = new ChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void stateChanged(ChangeEvent e) {
|
|
||||||
mMiscConfig.mBootSequenceLength_ms = (Integer)mBootSequenceLengthSpinner.getValue();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -28,6 +28,8 @@ import org.hyperion.hypercon.spec.TransformConfig;
|
|||||||
*/
|
*/
|
||||||
public class ColorTransformPanel extends JPanel {
|
public class ColorTransformPanel extends JPanel {
|
||||||
|
|
||||||
|
private final Dimension maxDim = new Dimension(1024, 20);
|
||||||
|
|
||||||
private final TransformConfig mColorConfig;
|
private final TransformConfig mColorConfig;
|
||||||
|
|
||||||
private JPanel mIndexPanel;
|
private JPanel mIndexPanel;
|
||||||
@ -99,7 +101,7 @@ public class ColorTransformPanel extends JPanel {
|
|||||||
mIndexPanel.add(mIndexLabel, BorderLayout.WEST);
|
mIndexPanel.add(mIndexLabel, BorderLayout.WEST);
|
||||||
|
|
||||||
mIndexField = new JTextField(mColorConfig.mLedIndexString);
|
mIndexField = new JTextField(mColorConfig.mLedIndexString);
|
||||||
mIndexField.setToolTipText("Comma seperated indices or index ranges (eg '1-10, 13, 14, 17-19')");
|
mIndexField.setToolTipText("Comma seperated indices or index ranges (eg '1-10, 13, 14, 17-19'); Special case '*', which means all leds");
|
||||||
mIndexField.getDocument().addDocumentListener(mDocumentListener);
|
mIndexField.getDocument().addDocumentListener(mDocumentListener);
|
||||||
mIndexPanel.add(mIndexField, BorderLayout.CENTER);
|
mIndexPanel.add(mIndexField, BorderLayout.CENTER);
|
||||||
}
|
}
|
||||||
@ -131,45 +133,57 @@ public class ColorTransformPanel extends JPanel {
|
|||||||
mRedTransformLabel = new JLabel("RED");
|
mRedTransformLabel = new JLabel("RED");
|
||||||
mRgbTransformPanel.add(mRedTransformLabel);
|
mRgbTransformPanel.add(mRedTransformLabel);
|
||||||
mRedThresholdSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mRedThreshold, 0.0, 1.0, 0.1));
|
mRedThresholdSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mRedThreshold, 0.0, 1.0, 0.1));
|
||||||
|
mRedThresholdSpinner.setMaximumSize(maxDim);
|
||||||
mRedThresholdSpinner.addChangeListener(mChangeListener);
|
mRedThresholdSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mRedThresholdSpinner);
|
mRgbTransformPanel.add(mRedThresholdSpinner);
|
||||||
mRedGammaSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mRedGamma, 0.0, 100.0, 0.1));
|
mRedGammaSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mRedGamma, 0.0, 100.0, 0.1));
|
||||||
mRedThresholdSpinner.addChangeListener(mChangeListener);
|
mRedGammaSpinner.setMaximumSize(maxDim);
|
||||||
|
mRedGammaSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mRedGammaSpinner);
|
mRgbTransformPanel.add(mRedGammaSpinner);
|
||||||
mRedBlacklevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mRedBlacklevel, 0.0, 1.0, 0.1));
|
mRedBlacklevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mRedBlacklevel, 0.0, 1.0, 0.1));
|
||||||
mRedThresholdSpinner.addChangeListener(mChangeListener);
|
mRedBlacklevelSpinner.setMaximumSize(maxDim);
|
||||||
|
mRedBlacklevelSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mRedBlacklevelSpinner);
|
mRgbTransformPanel.add(mRedBlacklevelSpinner);
|
||||||
mRedWhitelevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mRedWhitelevel, 0.0, 1.0, 0.1));
|
mRedWhitelevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mRedWhitelevel, 0.0, 1.0, 0.1));
|
||||||
mRedThresholdSpinner.addChangeListener(mChangeListener);
|
mRedWhitelevelSpinner.setMaximumSize(maxDim);
|
||||||
|
mRedWhitelevelSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mRedWhitelevelSpinner);
|
mRgbTransformPanel.add(mRedWhitelevelSpinner);
|
||||||
|
|
||||||
mGreenTransformLabel = new JLabel("GREEN");
|
mGreenTransformLabel = new JLabel("GREEN");
|
||||||
mRgbTransformPanel.add(mGreenTransformLabel);
|
mRgbTransformPanel.add(mGreenTransformLabel);
|
||||||
mGreenThresholdSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mGreenThreshold, 0.0, 1.0, 0.1));
|
mGreenThresholdSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mGreenThreshold, 0.0, 1.0, 0.1));
|
||||||
|
mGreenThresholdSpinner.setMaximumSize(maxDim);
|
||||||
mGreenThresholdSpinner.addChangeListener(mChangeListener);
|
mGreenThresholdSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mGreenThresholdSpinner);
|
mRgbTransformPanel.add(mGreenThresholdSpinner);
|
||||||
mGreenGammaSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mGreenGamma, 0.0, 100.0, 0.1));
|
mGreenGammaSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mGreenGamma, 0.0, 100.0, 0.1));
|
||||||
|
mGreenGammaSpinner.setMaximumSize(maxDim);
|
||||||
mGreenGammaSpinner.addChangeListener(mChangeListener);
|
mGreenGammaSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mGreenGammaSpinner);
|
mRgbTransformPanel.add(mGreenGammaSpinner);
|
||||||
mGreenBlacklevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mGreenBlacklevel, 0.0, 1.0, 0.1));
|
mGreenBlacklevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mGreenBlacklevel, 0.0, 1.0, 0.1));
|
||||||
|
mGreenBlacklevelSpinner.setMaximumSize(maxDim);
|
||||||
mGreenBlacklevelSpinner.addChangeListener(mChangeListener);
|
mGreenBlacklevelSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mGreenBlacklevelSpinner);
|
mRgbTransformPanel.add(mGreenBlacklevelSpinner);
|
||||||
mGreenWhitelevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mGreenWhitelevel, 0.0, 1.0, 0.1));
|
mGreenWhitelevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mGreenWhitelevel, 0.0, 1.0, 0.1));
|
||||||
|
mGreenWhitelevelSpinner.setMaximumSize(maxDim);
|
||||||
mGreenWhitelevelSpinner.addChangeListener(mChangeListener);
|
mGreenWhitelevelSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mGreenWhitelevelSpinner);
|
mRgbTransformPanel.add(mGreenWhitelevelSpinner);
|
||||||
|
|
||||||
mBlueTransformLabel = new JLabel("BLUE");
|
mBlueTransformLabel = new JLabel("BLUE");
|
||||||
mRgbTransformPanel.add(mBlueTransformLabel);
|
mRgbTransformPanel.add(mBlueTransformLabel);
|
||||||
mBlueThresholdSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mBlueThreshold, 0.0, 1.0, 0.1));
|
mBlueThresholdSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mBlueThreshold, 0.0, 1.0, 0.1));
|
||||||
|
mBlueThresholdSpinner.setMaximumSize(maxDim);
|
||||||
mBlueThresholdSpinner.addChangeListener(mChangeListener);
|
mBlueThresholdSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mBlueThresholdSpinner);
|
mRgbTransformPanel.add(mBlueThresholdSpinner);
|
||||||
mBlueGammaSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mBlueGamma, 0.0, 100.0, 0.1));
|
mBlueGammaSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mBlueGamma, 0.0, 100.0, 0.1));
|
||||||
|
mBlueGammaSpinner.setMaximumSize(maxDim);
|
||||||
mBlueGammaSpinner.addChangeListener(mChangeListener);
|
mBlueGammaSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mBlueGammaSpinner);
|
mRgbTransformPanel.add(mBlueGammaSpinner);
|
||||||
mBlueBlacklevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mBlueBlacklevel, 0.0, 1.0, 0.1));
|
mBlueBlacklevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mBlueBlacklevel, 0.0, 1.0, 0.1));
|
||||||
|
mBlueBlacklevelSpinner.setMaximumSize(maxDim);
|
||||||
mBlueBlacklevelSpinner.addChangeListener(mChangeListener);
|
mBlueBlacklevelSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mBlueBlacklevelSpinner);
|
mRgbTransformPanel.add(mBlueBlacklevelSpinner);
|
||||||
mBlueWhitelevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mBlueWhitelevel, 0.0, 1.0, 0.1));
|
mBlueWhitelevelSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mBlueWhitelevel, 0.0, 1.0, 0.1));
|
||||||
|
mBlueWhitelevelSpinner.setMaximumSize(maxDim);
|
||||||
mBlueWhitelevelSpinner.addChangeListener(mChangeListener);
|
mBlueWhitelevelSpinner.addChangeListener(mChangeListener);
|
||||||
mRgbTransformPanel.add(mBlueWhitelevelSpinner);
|
mRgbTransformPanel.add(mBlueWhitelevelSpinner);
|
||||||
}
|
}
|
||||||
@ -187,6 +201,7 @@ public class ColorTransformPanel extends JPanel {
|
|||||||
mHsvTransformPanel.add(mSaturationAdjustLabel);
|
mHsvTransformPanel.add(mSaturationAdjustLabel);
|
||||||
|
|
||||||
mSaturationAdjustSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mSaturationGain, 0.0, 1024.0, 0.01));
|
mSaturationAdjustSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mSaturationGain, 0.0, 1024.0, 0.01));
|
||||||
|
mSaturationAdjustSpinner.setMaximumSize(maxDim);
|
||||||
mSaturationAdjustSpinner.addChangeListener(mChangeListener);
|
mSaturationAdjustSpinner.addChangeListener(mChangeListener);
|
||||||
mHsvTransformPanel.add(mSaturationAdjustSpinner);
|
mHsvTransformPanel.add(mSaturationAdjustSpinner);
|
||||||
|
|
||||||
@ -194,6 +209,7 @@ public class ColorTransformPanel extends JPanel {
|
|||||||
mHsvTransformPanel.add(mValueAdjustLabel);
|
mHsvTransformPanel.add(mValueAdjustLabel);
|
||||||
|
|
||||||
mValueAdjustSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mValueGain, 0.0, 1024.0, 0.01));
|
mValueAdjustSpinner = new JSpinner(new SpinnerNumberModel(mColorConfig.mValueGain, 0.0, 1024.0, 0.01));
|
||||||
|
mValueAdjustSpinner.setMaximumSize(maxDim);
|
||||||
mValueAdjustSpinner.addChangeListener(mChangeListener);
|
mValueAdjustSpinner.addChangeListener(mChangeListener);
|
||||||
mHsvTransformPanel.add(mValueAdjustSpinner);
|
mHsvTransformPanel.add(mValueAdjustSpinner);
|
||||||
|
|
||||||
|
@ -171,7 +171,6 @@ public class ConfigPanel extends JPanel {
|
|||||||
mProcessPanel = new JPanel();
|
mProcessPanel = new JPanel();
|
||||||
mProcessPanel.setLayout(new BoxLayout(mProcessPanel, BoxLayout.Y_AXIS));
|
mProcessPanel.setLayout(new BoxLayout(mProcessPanel, BoxLayout.Y_AXIS));
|
||||||
|
|
||||||
mProcessPanel.add(new BootSequencePanel(ledString.mMiscConfig));
|
|
||||||
mProcessPanel.add(new FrameGrabberPanel(ledString.mMiscConfig));
|
mProcessPanel.add(new FrameGrabberPanel(ledString.mMiscConfig));
|
||||||
mProcessPanel.add(new ColorSmoothingPanel(ledString.mColorConfig));
|
mProcessPanel.add(new ColorSmoothingPanel(ledString.mColorConfig));
|
||||||
mProcessPanel.add(new ColorsPanel(ledString.mColorConfig));
|
mProcessPanel.add(new ColorsPanel(ledString.mColorConfig));
|
||||||
@ -187,6 +186,7 @@ public class ConfigPanel extends JPanel {
|
|||||||
|
|
||||||
mExternalPanel.add(new XbmcPanel(ledString.mMiscConfig));
|
mExternalPanel.add(new XbmcPanel(ledString.mMiscConfig));
|
||||||
mExternalPanel.add(new InterfacePanel(ledString.mMiscConfig));
|
mExternalPanel.add(new InterfacePanel(ledString.mMiscConfig));
|
||||||
|
mExternalPanel.add(new EffectEnginePanel(ledString.mMiscConfig));
|
||||||
mExternalPanel.add(Box.createVerticalGlue());
|
mExternalPanel.add(Box.createVerticalGlue());
|
||||||
}
|
}
|
||||||
return mExternalPanel;
|
return mExternalPanel;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.hyperion.hypercon.gui;
|
package org.hyperion.hypercon.gui;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
@ -10,11 +11,8 @@ import javax.swing.GroupLayout;
|
|||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JSpinner;
|
|
||||||
import javax.swing.SpinnerNumberModel;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
|
|
||||||
|
import org.hyperion.hypercon.gui.device.DeviceTypePanel;
|
||||||
import org.hyperion.hypercon.spec.ColorByteOrder;
|
import org.hyperion.hypercon.spec.ColorByteOrder;
|
||||||
import org.hyperion.hypercon.spec.DeviceConfig;
|
import org.hyperion.hypercon.spec.DeviceConfig;
|
||||||
import org.hyperion.hypercon.spec.DeviceType;
|
import org.hyperion.hypercon.spec.DeviceType;
|
||||||
@ -25,15 +23,10 @@ public class DevicePanel extends JPanel {
|
|||||||
|
|
||||||
private final DeviceConfig mDeviceConfig;
|
private final DeviceConfig mDeviceConfig;
|
||||||
|
|
||||||
|
|
||||||
private JLabel mTypeLabel;
|
private JLabel mTypeLabel;
|
||||||
private JComboBox<DeviceType> mTypeCombo;
|
private JComboBox<DeviceType> mTypeCombo;
|
||||||
|
|
||||||
private JLabel mOutputLabel;
|
private JPanel mDevicePanel;
|
||||||
private JComboBox<String> mOutputCombo;
|
|
||||||
|
|
||||||
private JLabel mBaudrateLabel;
|
|
||||||
private JSpinner mBaudrateSpinner;
|
|
||||||
|
|
||||||
private JLabel mRgbLabel;
|
private JLabel mRgbLabel;
|
||||||
private JComboBox<ColorByteOrder> mRgbCombo;
|
private JComboBox<ColorByteOrder> mRgbCombo;
|
||||||
@ -57,16 +50,8 @@ public class DevicePanel extends JPanel {
|
|||||||
private void initialise() {
|
private void initialise() {
|
||||||
setBorder(BorderFactory.createTitledBorder("Device"));
|
setBorder(BorderFactory.createTitledBorder("Device"));
|
||||||
|
|
||||||
mOutputLabel = new JLabel("Output");
|
mTypeLabel = new JLabel("Type: ");
|
||||||
add(mOutputLabel);
|
mTypeLabel.setMinimumSize(new Dimension(80, 10));
|
||||||
|
|
||||||
mOutputCombo = new JComboBox<>(KnownOutputs);
|
|
||||||
mOutputCombo.setEditable(true);
|
|
||||||
mOutputCombo.setSelectedItem(mDeviceConfig.mOutput);
|
|
||||||
mOutputCombo.addActionListener(mActionListener);
|
|
||||||
add(mOutputCombo);
|
|
||||||
|
|
||||||
mTypeLabel = new JLabel("LED Type:");
|
|
||||||
add(mTypeLabel);
|
add(mTypeLabel);
|
||||||
|
|
||||||
mTypeCombo = new JComboBox<>(DeviceType.values());
|
mTypeCombo = new JComboBox<>(DeviceType.values());
|
||||||
@ -74,14 +59,17 @@ public class DevicePanel extends JPanel {
|
|||||||
mTypeCombo.addActionListener(mActionListener);
|
mTypeCombo.addActionListener(mActionListener);
|
||||||
add(mTypeCombo);
|
add(mTypeCombo);
|
||||||
|
|
||||||
mBaudrateLabel = new JLabel("Baudrate");
|
mDevicePanel = new JPanel();
|
||||||
add(mBaudrateLabel);
|
mDevicePanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
|
||||||
|
mDevicePanel.setLayout(new BorderLayout());
|
||||||
|
DeviceTypePanel typePanel = mDeviceConfig.mType.getConfigPanel(mDeviceConfig);
|
||||||
|
if (typePanel != null) {
|
||||||
|
mDevicePanel.add(typePanel, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
add(mDevicePanel);
|
||||||
|
|
||||||
mBaudrateSpinner = new JSpinner(new SpinnerNumberModel(mDeviceConfig.mBaudrate, 1, 1000000, 128));
|
mRgbLabel = new JLabel("RGB Byte Order: ");
|
||||||
mBaudrateSpinner.addChangeListener(mChangeListener);
|
mRgbLabel.setMinimumSize(new Dimension(80, 10));
|
||||||
add(mBaudrateSpinner);
|
|
||||||
|
|
||||||
mRgbLabel = new JLabel("RGB Byte Order");
|
|
||||||
add(mRgbLabel);
|
add(mRgbLabel);
|
||||||
|
|
||||||
mRgbCombo = new JComboBox<>(ColorByteOrder.values());
|
mRgbCombo = new JComboBox<>(ColorByteOrder.values());
|
||||||
@ -89,52 +77,41 @@ public class DevicePanel extends JPanel {
|
|||||||
mRgbCombo.addActionListener(mActionListener);
|
mRgbCombo.addActionListener(mActionListener);
|
||||||
add(mRgbCombo);
|
add(mRgbCombo);
|
||||||
|
|
||||||
|
|
||||||
GroupLayout layout = new GroupLayout(this);
|
GroupLayout layout = new GroupLayout(this);
|
||||||
layout.setAutoCreateGaps(true);
|
layout.setAutoCreateGaps(true);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
layout.setHorizontalGroup(layout.createSequentialGroup()
|
layout.setHorizontalGroup(layout.createParallelGroup()
|
||||||
.addGroup(layout.createParallelGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addComponent(mOutputLabel)
|
|
||||||
.addComponent(mTypeLabel)
|
|
||||||
.addComponent(mBaudrateLabel)
|
|
||||||
.addComponent(mRgbLabel))
|
|
||||||
.addGroup(layout.createParallelGroup()
|
|
||||||
.addComponent(mOutputCombo)
|
|
||||||
.addComponent(mTypeCombo)
|
|
||||||
.addComponent(mBaudrateSpinner)
|
|
||||||
.addComponent(mRgbCombo))
|
|
||||||
);
|
|
||||||
layout.setVerticalGroup(layout.createSequentialGroup()
|
|
||||||
.addGroup(layout.createParallelGroup()
|
|
||||||
.addComponent(mOutputLabel)
|
|
||||||
.addComponent(mOutputCombo))
|
|
||||||
.addGroup(layout.createParallelGroup()
|
|
||||||
.addComponent(mTypeLabel)
|
.addComponent(mTypeLabel)
|
||||||
.addComponent(mTypeCombo))
|
.addComponent(mTypeCombo))
|
||||||
.addGroup(layout.createParallelGroup()
|
.addComponent(mDevicePanel)
|
||||||
.addComponent(mBaudrateLabel)
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addComponent(mBaudrateSpinner))
|
|
||||||
.addGroup(layout.createParallelGroup()
|
|
||||||
.addComponent(mRgbLabel)
|
.addComponent(mRgbLabel)
|
||||||
.addComponent(mRgbCombo)));
|
.addComponent(mRgbCombo)));
|
||||||
|
layout.setVerticalGroup(layout.createParallelGroup()
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(mTypeLabel)
|
||||||
|
.addComponent(mDevicePanel)
|
||||||
|
.addComponent(mRgbLabel))
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(mTypeCombo)
|
||||||
|
.addComponent(mDevicePanel)
|
||||||
|
.addComponent(mRgbCombo)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ActionListener mActionListener = new ActionListener() {
|
private final ActionListener mActionListener = new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
mDeviceConfig.mType = (DeviceType)mTypeCombo.getSelectedItem();
|
mDeviceConfig.mType = (DeviceType)mTypeCombo.getSelectedItem();
|
||||||
mDeviceConfig.mOutput = (String)mOutputCombo.getSelectedItem();
|
|
||||||
mDeviceConfig.mBaudrate = (Integer)mBaudrateSpinner.getValue();
|
|
||||||
mDeviceConfig.mColorByteOrder = (ColorByteOrder)mRgbCombo.getSelectedItem();
|
mDeviceConfig.mColorByteOrder = (ColorByteOrder)mRgbCombo.getSelectedItem();
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final ChangeListener mChangeListener = new ChangeListener() {
|
mDevicePanel.removeAll();
|
||||||
@Override
|
DeviceTypePanel typePanel = mDeviceConfig.mType.getConfigPanel(mDeviceConfig);
|
||||||
public void stateChanged(ChangeEvent e) {
|
if (typePanel != null) {
|
||||||
mDeviceConfig.mBaudrate = (Integer)mBaudrateSpinner.getValue();
|
mDevicePanel.add(typePanel, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
revalidate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,175 @@
|
|||||||
|
package org.hyperion.hypercon.gui;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.GroupLayout;
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JSpinner;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.SpinnerNumberModel;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
|
||||||
|
import org.hyperion.hypercon.spec.MiscConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* THe EffectEnginePanel contains the components for configuring the parameters of the Effect Engine
|
||||||
|
*/
|
||||||
|
public class EffectEnginePanel extends JPanel {
|
||||||
|
|
||||||
|
/** The MISC config contains the effect engine settings */
|
||||||
|
private final MiscConfig mMiscConfig;
|
||||||
|
|
||||||
|
private JLabel mPathLabel;
|
||||||
|
private JTextField mPathField;
|
||||||
|
|
||||||
|
private JPanel mBootSequencePanel;
|
||||||
|
private JCheckBox mBootSequenceCheck;
|
||||||
|
private JLabel mBootSequenceLabel;
|
||||||
|
private JTextField mBootSequenceField;
|
||||||
|
private JLabel mBootSequenceLengthLabel;
|
||||||
|
private JSpinner mBootSequenceLengthSpinner;
|
||||||
|
|
||||||
|
public EffectEnginePanel(final MiscConfig pMiscConfig) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
mMiscConfig = pMiscConfig;
|
||||||
|
|
||||||
|
initialise();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialise() {
|
||||||
|
setBorder(BorderFactory.createTitledBorder("Effect Engine"));
|
||||||
|
|
||||||
|
mPathLabel = new JLabel("Directory: ");
|
||||||
|
mPathLabel.setMinimumSize(new Dimension(80, 10));
|
||||||
|
add(mPathLabel);
|
||||||
|
|
||||||
|
mPathField = new JTextField();
|
||||||
|
mPathField.setMaximumSize(new Dimension(1024, 20));
|
||||||
|
mPathField.setText(mMiscConfig.mEffectEnginePath);
|
||||||
|
mPathField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
mMiscConfig.mEffectEnginePath = mPathField.getText();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
mMiscConfig.mEffectEnginePath = mPathField.getText();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
mMiscConfig.mEffectEnginePath = mPathField.getText();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
add(mPathField);
|
||||||
|
|
||||||
|
add(getBootSequencePanel());
|
||||||
|
|
||||||
|
GroupLayout layout = new GroupLayout(this);
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
layout.setVerticalGroup(layout.createSequentialGroup()
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mPathLabel)
|
||||||
|
.addComponent(mPathField))
|
||||||
|
.addComponent(getBootSequencePanel()));
|
||||||
|
|
||||||
|
layout.setHorizontalGroup(layout.createParallelGroup()
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(mPathLabel)
|
||||||
|
.addComponent(mPathField))
|
||||||
|
.addComponent(getBootSequencePanel()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private JPanel getBootSequencePanel() {
|
||||||
|
if (mBootSequencePanel == null) {
|
||||||
|
mBootSequencePanel = new JPanel();
|
||||||
|
mBootSequencePanel.setBorder(BorderFactory.createTitledBorder("Bootsequence"));
|
||||||
|
|
||||||
|
mBootSequenceCheck = new JCheckBox("Enabled");
|
||||||
|
mBootSequenceCheck.setSelected(mMiscConfig.mBootSequenceEnabled);
|
||||||
|
mBootSequenceCheck.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
mMiscConfig.mBootSequenceEnabled = mBootSequenceCheck.isSelected();
|
||||||
|
mBootSequenceLabel.setEnabled(mMiscConfig.mBootSequenceEnabled);
|
||||||
|
mBootSequenceField.setEnabled(mMiscConfig.mBootSequenceEnabled);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mBootSequencePanel.add(mBootSequenceCheck);
|
||||||
|
|
||||||
|
mBootSequenceLabel = new JLabel("Type:");
|
||||||
|
mBootSequenceLabel.setMinimumSize(new Dimension(75, 10));
|
||||||
|
mBootSequenceLabel.setEnabled(mMiscConfig.mBootSequenceEnabled);
|
||||||
|
mBootSequencePanel.add(mBootSequenceLabel);
|
||||||
|
|
||||||
|
mBootSequenceField = new JTextField();
|
||||||
|
mBootSequenceField.setMaximumSize(new Dimension(1024, 20));
|
||||||
|
mBootSequenceField.setText(mMiscConfig.mBootSequenceEffect);
|
||||||
|
mBootSequenceField.setEnabled(mMiscConfig.mBootSequenceEnabled);
|
||||||
|
mBootSequenceField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
mMiscConfig.mBootSequenceEffect = mBootSequenceField.getText();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
mMiscConfig.mBootSequenceEffect = mBootSequenceField.getText();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
mMiscConfig.mBootSequenceEffect = mBootSequenceField.getText();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mBootSequencePanel.add(mBootSequenceField);
|
||||||
|
|
||||||
|
mBootSequenceLengthLabel = new JLabel("Length[ms]: ");
|
||||||
|
mBootSequenceLengthLabel.setMinimumSize(new Dimension(75, 10));
|
||||||
|
mBootSequenceLengthLabel.setEnabled(mMiscConfig.mBootSequenceEnabled);
|
||||||
|
mBootSequencePanel.add(mBootSequenceLengthLabel);
|
||||||
|
|
||||||
|
mBootSequenceLengthSpinner = new JSpinner(new SpinnerNumberModel(mMiscConfig.mBootSequenceLength_ms, 100, 1500000, 500));
|
||||||
|
mBootSequenceLengthSpinner.setMaximumSize(new Dimension(1024, 20));
|
||||||
|
mBootSequenceLengthSpinner.setEnabled(mMiscConfig.mBootSequenceEnabled);
|
||||||
|
mBootSequenceLengthSpinner.addChangeListener(new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
mMiscConfig.mBootSequenceLength_ms = (Integer)mBootSequenceLengthSpinner.getValue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mBootSequencePanel.add(mBootSequenceLengthSpinner);
|
||||||
|
|
||||||
|
GroupLayout layout = new GroupLayout(mBootSequencePanel);
|
||||||
|
mBootSequencePanel.setLayout(layout);
|
||||||
|
|
||||||
|
layout.setVerticalGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(mBootSequenceCheck)
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mBootSequenceLabel)
|
||||||
|
.addComponent(mBootSequenceField))
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mBootSequenceLengthLabel)
|
||||||
|
.addComponent(mBootSequenceLengthSpinner))
|
||||||
|
);
|
||||||
|
|
||||||
|
layout.setHorizontalGroup(layout.createSequentialGroup()
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mBootSequenceCheck)
|
||||||
|
.addComponent(mBootSequenceLabel)
|
||||||
|
.addComponent(mBootSequenceLengthLabel))
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mBootSequenceCheck)
|
||||||
|
.addComponent(mBootSequenceField)
|
||||||
|
.addComponent(mBootSequenceLengthSpinner)));
|
||||||
|
}
|
||||||
|
return mBootSequencePanel;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package org.hyperion.hypercon.gui.device;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
import org.hyperion.hypercon.spec.DeviceConfig;
|
||||||
|
|
||||||
|
public abstract class DeviceTypePanel extends JPanel {
|
||||||
|
|
||||||
|
protected final Dimension firstColMinDim = new Dimension(80, 10);
|
||||||
|
protected final Dimension maxDim = new Dimension(1024, 20);
|
||||||
|
|
||||||
|
protected DeviceConfig mDeviceConfig = null;
|
||||||
|
|
||||||
|
public DeviceTypePanel() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceConfig(DeviceConfig pDeviceConfig) {
|
||||||
|
mDeviceConfig = pDeviceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package org.hyperion.hypercon.gui.device;
|
||||||
|
|
||||||
|
import javax.swing.GroupLayout;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
|
||||||
|
import org.hyperion.hypercon.spec.DeviceConfig;
|
||||||
|
|
||||||
|
public class LightPackPanel extends DeviceTypePanel {
|
||||||
|
|
||||||
|
private JLabel mSerialNoLabel;
|
||||||
|
private JTextField mSerialNoField;
|
||||||
|
|
||||||
|
public LightPackPanel() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
initialise();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDeviceConfig(DeviceConfig pDeviceConfig) {
|
||||||
|
super.setDeviceConfig(pDeviceConfig);
|
||||||
|
|
||||||
|
mSerialNoField.setText(mDeviceConfig.mOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialise() {
|
||||||
|
mSerialNoLabel = new JLabel("Serial #: ");
|
||||||
|
mSerialNoLabel.setMinimumSize(firstColMinDim);
|
||||||
|
add(mSerialNoLabel);
|
||||||
|
|
||||||
|
mSerialNoField = new JTextField();
|
||||||
|
mSerialNoField.setMaximumSize(maxDim);
|
||||||
|
mSerialNoField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
mDeviceConfig.mOutput = mSerialNoField.getText();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
mDeviceConfig.mOutput = mSerialNoField.getText();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
mDeviceConfig.mOutput = mSerialNoField.getText();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
add(mSerialNoField);
|
||||||
|
|
||||||
|
GroupLayout layout = new GroupLayout(this);
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
layout.setHorizontalGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(mSerialNoLabel)
|
||||||
|
.addComponent(mSerialNoField));
|
||||||
|
layout.setVerticalGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mSerialNoLabel)
|
||||||
|
.addComponent(mSerialNoField));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
package org.hyperion.hypercon.gui.device;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
|
import javax.swing.GroupLayout;
|
||||||
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JSpinner;
|
||||||
|
import javax.swing.SpinnerNumberModel;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import org.hyperion.hypercon.spec.DeviceConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel for configuring Ws2801 specific settings
|
||||||
|
*/
|
||||||
|
public class SerialPanel extends DeviceTypePanel {
|
||||||
|
|
||||||
|
public static final String[] KnownOutputs = { "/dev/ttyS0", "/dev/ttyUSB0", "/dev/ttyAMA0", "/dev/null"};
|
||||||
|
|
||||||
|
private JLabel mOutputLabel;
|
||||||
|
private JComboBox<String> mOutputCombo;
|
||||||
|
|
||||||
|
private JLabel mBaudrateLabel;
|
||||||
|
private JSpinner mBaudrateSpinner;
|
||||||
|
|
||||||
|
|
||||||
|
public SerialPanel() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
initialise();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDeviceConfig(DeviceConfig pDeviceConfig) {
|
||||||
|
super.setDeviceConfig(pDeviceConfig);
|
||||||
|
|
||||||
|
mOutputCombo.setSelectedItem(mDeviceConfig.mOutput);
|
||||||
|
((SpinnerNumberModel)mBaudrateSpinner.getModel()).setValue(mDeviceConfig.mBaudrate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialise() {
|
||||||
|
mOutputLabel = new JLabel("Output: ");
|
||||||
|
mOutputLabel.setMinimumSize(firstColMinDim);
|
||||||
|
add(mOutputLabel);
|
||||||
|
|
||||||
|
mOutputCombo = new JComboBox<>(KnownOutputs);
|
||||||
|
mOutputCombo.setMaximumSize(maxDim);
|
||||||
|
mOutputCombo.setEditable(true);
|
||||||
|
mOutputCombo.addActionListener(mActionListener);
|
||||||
|
add(mOutputCombo);
|
||||||
|
|
||||||
|
mBaudrateLabel = new JLabel("Baudrate: ");
|
||||||
|
mBaudrateLabel.setMinimumSize(firstColMinDim);
|
||||||
|
add(mBaudrateLabel);
|
||||||
|
|
||||||
|
mBaudrateSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 1000000, 128));
|
||||||
|
mBaudrateSpinner .setMaximumSize(maxDim);
|
||||||
|
mBaudrateSpinner.addChangeListener(mChangeListener);
|
||||||
|
add(mBaudrateSpinner);
|
||||||
|
|
||||||
|
|
||||||
|
GroupLayout layout = new GroupLayout(this);
|
||||||
|
layout.setAutoCreateGaps(true);
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
layout.setHorizontalGroup(layout.createSequentialGroup()
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mOutputLabel)
|
||||||
|
.addComponent(mBaudrateLabel))
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mOutputCombo)
|
||||||
|
.addComponent(mBaudrateSpinner))
|
||||||
|
);
|
||||||
|
layout.setVerticalGroup(layout.createSequentialGroup()
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mOutputLabel)
|
||||||
|
.addComponent(mOutputCombo))
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mBaudrateLabel)
|
||||||
|
.addComponent(mBaudrateSpinner))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionListener mActionListener = new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if (e.getSource() == mOutputCombo) {
|
||||||
|
mDeviceConfig.mOutput = (String)mOutputCombo.getSelectedItem();
|
||||||
|
} else if (e.getSource() == mBaudrateSpinner) {
|
||||||
|
mDeviceConfig.mBaudrate = (Integer)mBaudrateSpinner.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private ChangeListener mChangeListener = new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
mDeviceConfig.mBaudrate = (Integer)mBaudrateSpinner.getValue();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package org.hyperion.hypercon.gui.device;
|
||||||
|
|
||||||
|
import javax.swing.GroupLayout;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
|
||||||
|
import org.hyperion.hypercon.spec.DeviceConfig;
|
||||||
|
|
||||||
|
public class TestDevicePanel extends DeviceTypePanel {
|
||||||
|
|
||||||
|
private JLabel mFilenameLabel;
|
||||||
|
private JTextField mFilenameField;
|
||||||
|
|
||||||
|
public TestDevicePanel() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
initialise();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDeviceConfig(DeviceConfig pDeviceConfig) {
|
||||||
|
super.setDeviceConfig(pDeviceConfig);
|
||||||
|
|
||||||
|
mFilenameField.setText(mDeviceConfig.mOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialise() {
|
||||||
|
mFilenameLabel = new JLabel("Filename: ");
|
||||||
|
mFilenameLabel.setMinimumSize(firstColMinDim);
|
||||||
|
add(mFilenameLabel);
|
||||||
|
|
||||||
|
mFilenameField = new JTextField();
|
||||||
|
mFilenameField.setMaximumSize(maxDim);
|
||||||
|
mFilenameField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
mDeviceConfig.mOutput = mFilenameField.getText();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
mDeviceConfig.mOutput = mFilenameField.getText();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
mDeviceConfig.mOutput = mFilenameField.getText();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
add(mFilenameField);
|
||||||
|
|
||||||
|
GroupLayout layout = new GroupLayout(this);
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
layout.setHorizontalGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(mFilenameLabel)
|
||||||
|
.addComponent(mFilenameField));
|
||||||
|
layout.setVerticalGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mFilenameLabel)
|
||||||
|
.addComponent(mFilenameField));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
package org.hyperion.hypercon.gui.device;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
|
import javax.swing.GroupLayout;
|
||||||
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JSpinner;
|
||||||
|
import javax.swing.SpinnerNumberModel;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import org.hyperion.hypercon.spec.DeviceConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel for configuring Ws2801 specific settings
|
||||||
|
*/
|
||||||
|
public class Ws2801Panel extends DeviceTypePanel {
|
||||||
|
|
||||||
|
public static final String[] KnownOutputs = {"/dev/spidev0.0", "/dev/spidev0.1", "/dev/null"};
|
||||||
|
|
||||||
|
private JLabel mOutputLabel;
|
||||||
|
private JComboBox<String> mOutputCombo;
|
||||||
|
|
||||||
|
private JLabel mBaudrateLabel;
|
||||||
|
private JSpinner mBaudrateSpinner;
|
||||||
|
|
||||||
|
|
||||||
|
public Ws2801Panel() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
initialise();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDeviceConfig(DeviceConfig pDeviceConfig) {
|
||||||
|
super.setDeviceConfig(pDeviceConfig);
|
||||||
|
|
||||||
|
mOutputCombo.setSelectedItem(mDeviceConfig.mOutput);
|
||||||
|
((SpinnerNumberModel)mBaudrateSpinner.getModel()).setValue(mDeviceConfig.mBaudrate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialise() {
|
||||||
|
mOutputLabel = new JLabel("Output: ");
|
||||||
|
mOutputLabel.setMinimumSize(firstColMinDim);
|
||||||
|
add(mOutputLabel);
|
||||||
|
|
||||||
|
mOutputCombo = new JComboBox<>(KnownOutputs);
|
||||||
|
mOutputCombo.setMaximumSize(maxDim);
|
||||||
|
mOutputCombo.setEditable(true);
|
||||||
|
mOutputCombo.addActionListener(mActionListener);
|
||||||
|
add(mOutputCombo);
|
||||||
|
|
||||||
|
mBaudrateLabel = new JLabel("Baudrate: ");
|
||||||
|
mBaudrateLabel.setMinimumSize(firstColMinDim);
|
||||||
|
add(mBaudrateLabel);
|
||||||
|
|
||||||
|
mBaudrateSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 1000000, 128));
|
||||||
|
mBaudrateSpinner.setMaximumSize(maxDim);
|
||||||
|
mBaudrateSpinner.addChangeListener(mChangeListener);
|
||||||
|
add(mBaudrateSpinner);
|
||||||
|
|
||||||
|
|
||||||
|
GroupLayout layout = new GroupLayout(this);
|
||||||
|
layout.setAutoCreateGaps(true);
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
layout.setHorizontalGroup(layout.createSequentialGroup()
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mOutputLabel)
|
||||||
|
.addComponent(mBaudrateLabel))
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mOutputCombo)
|
||||||
|
.addComponent(mBaudrateSpinner))
|
||||||
|
);
|
||||||
|
layout.setVerticalGroup(layout.createSequentialGroup()
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mOutputLabel)
|
||||||
|
.addComponent(mOutputCombo))
|
||||||
|
.addGroup(layout.createParallelGroup()
|
||||||
|
.addComponent(mBaudrateLabel)
|
||||||
|
.addComponent(mBaudrateSpinner))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionListener mActionListener = new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if (e.getSource() == mOutputCombo) {
|
||||||
|
mDeviceConfig.mOutput = (String)mOutputCombo.getSelectedItem();
|
||||||
|
} else if (e.getSource() == mBaudrateSpinner) {
|
||||||
|
mDeviceConfig.mBaudrate = (Integer)mBaudrateSpinner.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private ChangeListener mChangeListener = new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
mDeviceConfig.mBaudrate = (Integer)mBaudrateSpinner.getValue();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
package org.hyperion.hypercon.spec;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enumeration of possible boot sequences
|
|
||||||
*/
|
|
||||||
public enum BootSequence {
|
|
||||||
/** The rainbow boot sequence */
|
|
||||||
rainbow,
|
|
||||||
/** The Knight Rider (or KITT) boot sequence */
|
|
||||||
knightrider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of the BootSequence
|
|
||||||
*
|
|
||||||
* @return String representation of this boot-sequence
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
switch(this) {
|
|
||||||
case rainbow:
|
|
||||||
return "Rainbow";
|
|
||||||
case knightrider:
|
|
||||||
return "Knight Rider";
|
|
||||||
}
|
|
||||||
return "None";
|
|
||||||
}
|
|
||||||
}
|
|
@ -47,8 +47,8 @@ public class ColorConfig {
|
|||||||
strBuf.append("\t/// - 'gamma' The gamma-curve correction factor\n");
|
strBuf.append("\t/// - 'gamma' The gamma-curve correction factor\n");
|
||||||
strBuf.append("\t/// - 'blacklevel' The lowest possible value (when the channel is black)\n");
|
strBuf.append("\t/// - 'blacklevel' The lowest possible value (when the channel is black)\n");
|
||||||
strBuf.append("\t/// - 'whitelevel' The highest possible value (when the channel is white)\n");
|
strBuf.append("\t/// - 'whitelevel' The highest possible value (when the channel is white)\n");
|
||||||
strBuf.append("\t///");
|
strBuf.append("\t///\n");
|
||||||
strBuf.append("\t/// Next to the list with color transforms there is also a smoothing option.");
|
strBuf.append("\t/// Next to the list with color transforms there is also a smoothing option.\n");
|
||||||
strBuf.append("\t/// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning \n");
|
strBuf.append("\t/// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning \n");
|
||||||
strBuf.append("\t/// parameters:\n");
|
strBuf.append("\t/// parameters:\n");
|
||||||
strBuf.append("\t/// - 'type' The type of smoothing algorithm ('linear' or 'none')\n");
|
strBuf.append("\t/// - 'type' The type of smoothing algorithm ('linear' or 'none')\n");
|
||||||
|
@ -12,7 +12,7 @@ public class DeviceConfig {
|
|||||||
/** The device 'file' name */
|
/** The device 'file' name */
|
||||||
public String mOutput = "/dev/spidev0.0";
|
public String mOutput = "/dev/spidev0.0";
|
||||||
/** The baudrate of the device */
|
/** The baudrate of the device */
|
||||||
public int mBaudrate = 1000000;
|
public int mBaudrate = 250000;
|
||||||
/** The order of the color bytes */
|
/** The order of the color bytes */
|
||||||
public ColorByteOrder mColorByteOrder = ColorByteOrder.RGB;
|
public ColorByteOrder mColorByteOrder = ColorByteOrder.RGB;
|
||||||
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
package org.hyperion.hypercon.spec;
|
package org.hyperion.hypercon.spec;
|
||||||
|
|
||||||
|
import org.hyperion.hypercon.gui.device.DeviceTypePanel;
|
||||||
|
import org.hyperion.hypercon.gui.device.LightPackPanel;
|
||||||
|
import org.hyperion.hypercon.gui.device.SerialPanel;
|
||||||
|
import org.hyperion.hypercon.gui.device.TestDevicePanel;
|
||||||
|
import org.hyperion.hypercon.gui.device.Ws2801Panel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumeration of known device types
|
* Enumeration of known device types
|
||||||
*/
|
*/
|
||||||
@ -12,21 +18,70 @@ public enum DeviceType {
|
|||||||
lpd6803("LPD6803"),
|
lpd6803("LPD6803"),
|
||||||
/** SEDU LED device */
|
/** SEDU LED device */
|
||||||
sedu("SEDU"),
|
sedu("SEDU"),
|
||||||
|
/** Lightberry device */
|
||||||
|
lightberry("Lightberry"),
|
||||||
/** Adalight device */
|
/** Adalight device */
|
||||||
adalight("Adalight"),
|
adalight("Adalight"),
|
||||||
/** Lightpack USB led device */
|
/** Lightpack USB led device */
|
||||||
lightpack("Lightpack"),
|
lightpack("Lightpack"),
|
||||||
|
/** Paintpack USB led device */
|
||||||
|
paintpack("Paintpack"),
|
||||||
/** Test device for writing color values to file-output */
|
/** Test device for writing color values to file-output */
|
||||||
test("Test"),
|
test("Test"),
|
||||||
/** No device, no output is generated */
|
/** No device, no output is generated */
|
||||||
none("None");
|
none("None");
|
||||||
|
|
||||||
|
/** The 'pretty' name of the device type */
|
||||||
private final String mName;
|
private final String mName;
|
||||||
|
|
||||||
private DeviceType(String name) {
|
/** The device specific configuration panel */
|
||||||
|
private DeviceTypePanel mConfigPanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the DeviceType
|
||||||
|
*
|
||||||
|
* @param name The 'pretty' name of the device type
|
||||||
|
* @param pConfigPanel The panel for device type specific configuration
|
||||||
|
*/
|
||||||
|
private DeviceType(final String name) {
|
||||||
mName = name;
|
mName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configuration panel for the this device-type (or null if no configuration is required)
|
||||||
|
*
|
||||||
|
* @return The panel for configuring this device type
|
||||||
|
*/
|
||||||
|
public DeviceTypePanel getConfigPanel(DeviceConfig pDeviceConfig) {
|
||||||
|
if (mConfigPanel == null) {
|
||||||
|
switch (this) {
|
||||||
|
case ws2801:
|
||||||
|
case lightberry:
|
||||||
|
case lpd6803:
|
||||||
|
case lpd8806:
|
||||||
|
mConfigPanel = new Ws2801Panel();
|
||||||
|
break;
|
||||||
|
case test:
|
||||||
|
mConfigPanel = new TestDevicePanel();
|
||||||
|
break;
|
||||||
|
case adalight:
|
||||||
|
case sedu:
|
||||||
|
mConfigPanel = new SerialPanel();
|
||||||
|
break;
|
||||||
|
case lightpack:
|
||||||
|
mConfigPanel = new LightPackPanel();
|
||||||
|
break;
|
||||||
|
case paintpack:
|
||||||
|
case none:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mConfigPanel != null) {
|
||||||
|
mConfigPanel.setDeviceConfig(pDeviceConfig);
|
||||||
|
}
|
||||||
|
return mConfigPanel;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mName;
|
return mName;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package org.hyperion.hypercon.spec;
|
package org.hyperion.hypercon.spec;
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Observable;
|
import java.util.Observable;
|
||||||
|
|
||||||
|
import org.hyperion.hypercon.JsonStringBuffer;
|
||||||
import org.hyperion.hypercon.LedFrameFactory;
|
import org.hyperion.hypercon.LedFrameFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,17 +161,15 @@ public class ImageProcessConfig extends Observable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBlackborderJson() {
|
public void appendTo(JsonStringBuffer pJsonBuf) {
|
||||||
StringBuffer strBuf = new StringBuffer();
|
String comment =
|
||||||
|
"The black border configuration, contains the following items: \n" +
|
||||||
|
" * enable : true if the detector should be activated\n";
|
||||||
|
pJsonBuf.writeComment(comment);
|
||||||
|
|
||||||
strBuf.append("\t/// The black border configuration, contains the following items: \n");
|
pJsonBuf.startObject("blackborderdetector");
|
||||||
strBuf.append("\t/// * enable : true if the detector should be activated\n");
|
pJsonBuf.addValue("enable", mBlackBorderRemoval, true);
|
||||||
|
pJsonBuf.stopObject();
|
||||||
strBuf.append("\t\"blackborderdetector\" :\n");
|
|
||||||
strBuf.append("\t{\n");
|
|
||||||
strBuf.append(String.format(Locale.ROOT, "\t\t\"enable\" : %s\n", mBlackBorderRemoval ? "true" : "false"));
|
|
||||||
strBuf.append("\t}");
|
|
||||||
|
|
||||||
return strBuf.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
package org.hyperion.hypercon.spec;
|
package org.hyperion.hypercon.spec;
|
||||||
|
|
||||||
import java.util.Locale;
|
import org.hyperion.hypercon.JsonStringBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Miscellaneous configuration items for the Hyperion daemon.
|
* Miscellaneous configuration items for the Hyperion daemon.
|
||||||
*/
|
*/
|
||||||
public class MiscConfig {
|
public class MiscConfig {
|
||||||
|
|
||||||
/** Flag indicating that the boot sequence is enabled */
|
/** The absolute location(s) of the effects */
|
||||||
public boolean mBootsequenceEnabled = true;
|
public String mEffectEnginePath = "/opt/hyperion/effects";
|
||||||
/** The selected boot sequence */
|
|
||||||
public BootSequence mBootSequence = BootSequence.rainbow;
|
/** Flag indicating that the boot sequence is enabled(true) or not(false) */
|
||||||
/** The length of the boot sequence [ms] */
|
public boolean mBootSequenceEnabled = true;
|
||||||
|
/** The effect selected for the boot sequence */
|
||||||
|
public String mBootSequenceEffect = "Rainbow swirl fast";
|
||||||
|
/** The (maximum) length of the boot-sequence */
|
||||||
public int mBootSequenceLength_ms = 3000;
|
public int mBootSequenceLength_ms = 3000;
|
||||||
|
|
||||||
/** Flag indicating that the Frame Grabber is enabled */
|
/** Flag indicating that the Frame Grabber is enabled */
|
||||||
@ -53,89 +56,128 @@ public class MiscConfig {
|
|||||||
/** The TCP port at which the Protobuf server is listening for incoming connections */
|
/** The TCP port at which the Protobuf server is listening for incoming connections */
|
||||||
public int mBoblightPort = 19333;
|
public int mBoblightPort = 19333;
|
||||||
|
|
||||||
|
public void appendTo(JsonStringBuffer strBuf) {
|
||||||
|
String effectEngineComment =
|
||||||
|
"The configuration of the effect engine, contains the following items: \n" +
|
||||||
|
" * paths : An array with absolute location(s) of directories with effects \n" +
|
||||||
|
" * bootsequence : The effect selected as 'boot sequence'";
|
||||||
|
strBuf.writeComment(effectEngineComment);
|
||||||
|
|
||||||
|
String[] effectPaths = mEffectEnginePath.split(":");
|
||||||
|
|
||||||
|
strBuf.startObject("effects");
|
||||||
|
strBuf.startArray("paths");
|
||||||
|
for (String effectPath : effectPaths) {
|
||||||
|
strBuf.addArrayElement(effectPath, effectPath == effectPaths[effectPaths.length-1]);
|
||||||
|
}
|
||||||
|
strBuf.stopArray(true);
|
||||||
|
strBuf.stopObject();
|
||||||
|
|
||||||
|
strBuf.newLine();
|
||||||
|
|
||||||
|
strBuf.toggleComment(!mBootSequenceEnabled);
|
||||||
|
strBuf.startObject("bootsequence");
|
||||||
|
strBuf.addValue("effect", mBootSequenceEffect, false);
|
||||||
|
strBuf.addValue("duration_ms", mBootSequenceLength_ms, true);
|
||||||
|
strBuf.stopObject();
|
||||||
|
strBuf.toggleComment(false);
|
||||||
|
|
||||||
|
strBuf.newLine();
|
||||||
|
|
||||||
|
String grabComment =
|
||||||
|
" The configuration for the frame-grabber, contains the following items: \n" +
|
||||||
|
" * width : The width of the grabbed frames [pixels]\n" +
|
||||||
|
" * height : The height of the grabbed frames [pixels]\n" +
|
||||||
|
" * frequency_Hz : The frequency of the frame grab [Hz]\n";
|
||||||
|
strBuf.writeComment(grabComment);
|
||||||
|
|
||||||
|
strBuf.toggleComment(!mFrameGrabberEnabled);
|
||||||
|
strBuf.startObject("framegrabber");
|
||||||
|
strBuf.addValue("width", mFrameGrabberWidth, false);
|
||||||
|
strBuf.addValue("height", mFrameGrabberHeight, false);
|
||||||
|
strBuf.addValue("frequency_Hz", 1000.0/mFrameGrabberInterval_ms, true);
|
||||||
|
strBuf.stopObject();
|
||||||
|
strBuf.toggleComment(false);
|
||||||
|
|
||||||
|
strBuf.newLine();
|
||||||
|
|
||||||
|
String xbmcComment =
|
||||||
|
"The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields: \n" +
|
||||||
|
" * xbmcAddress : The IP address of the XBMC-host\n" +
|
||||||
|
" * xbmcTcpPort : The TCP-port of the XBMC-server\n" +
|
||||||
|
" * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback\n" +
|
||||||
|
" * grabPictures : Flag indicating that the frame-grabber is on(true) during picture show\n" +
|
||||||
|
" * grabAudio : Flag indicating that the frame-grabber is on(true) during audio playback\n" +
|
||||||
|
" * grabMenu : Flag indicating that the frame-grabber is on(true) in the XBMC menu\n";
|
||||||
|
strBuf.writeComment(xbmcComment);
|
||||||
|
|
||||||
|
strBuf.toggleComment(!mXbmcCheckerEnabled);
|
||||||
|
strBuf.startObject("xbmcVideoChecker");
|
||||||
|
strBuf.addValue("xbmcAddress", mXbmcAddress, false);
|
||||||
|
strBuf.addValue("xbmcTcpPort", mXbmcTcpPort, false);
|
||||||
|
strBuf.addValue("grabVideo", mVideoOn, false);
|
||||||
|
strBuf.addValue("grabPictures", mPictureOn, false);
|
||||||
|
strBuf.addValue("grabAudio", mAudioOn, false);
|
||||||
|
strBuf.addValue("grabMenu", mMenuOn, true);
|
||||||
|
strBuf.stopObject();
|
||||||
|
strBuf.toggleComment(false);
|
||||||
|
|
||||||
|
strBuf.newLine();
|
||||||
|
|
||||||
|
String jsonComment =
|
||||||
|
"The configuration of the Json server which enables the json remote interface\n" +
|
||||||
|
" * port : Port at which the json server is started\n";
|
||||||
|
strBuf.writeComment(jsonComment);
|
||||||
|
|
||||||
|
strBuf.toggleComment(!mJsonInterfaceEnabled);
|
||||||
|
strBuf.startObject("jsonServer");
|
||||||
|
strBuf.addValue("port", mJsonPort, true);
|
||||||
|
strBuf.stopObject();
|
||||||
|
strBuf.toggleComment(false);
|
||||||
|
|
||||||
|
strBuf.newLine();
|
||||||
|
|
||||||
|
String protoComment =
|
||||||
|
"The configuration of the Proto server which enables the protobuffer remote interface\n" +
|
||||||
|
" * port : Port at which the protobuffer server is started\n";
|
||||||
|
strBuf.writeComment(protoComment);
|
||||||
|
|
||||||
|
strBuf.toggleComment(!mProtoInterfaceEnabled);
|
||||||
|
strBuf.startObject("protoServer");
|
||||||
|
strBuf.addValue("port", mProtoPort, true);
|
||||||
|
strBuf.stopObject();
|
||||||
|
strBuf.toggleComment(false);
|
||||||
|
|
||||||
|
strBuf.newLine();
|
||||||
|
|
||||||
|
String boblightComment =
|
||||||
|
"The configuration of the boblight server which enables the boblight remote interface\n" +
|
||||||
|
" * port : Port at which the boblight server is started\n";
|
||||||
|
strBuf.writeComment(boblightComment);
|
||||||
|
|
||||||
|
strBuf.toggleComment(!mBoblightInterfaceEnabled);
|
||||||
|
strBuf.startObject("boblightServer");
|
||||||
|
strBuf.addValue("port", mBoblightPort, true);
|
||||||
|
strBuf.stopObject();
|
||||||
|
strBuf.toggleComment(false);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Creates the JSON string of the configuration as used in the Hyperion daemon configfile
|
* Creates the JSON string of the configuration as used in the Hyperion daemon configfile
|
||||||
*
|
*
|
||||||
* @return The JSON string of this MiscConfig
|
* @return The JSON string of this MiscConfig
|
||||||
*/
|
*/
|
||||||
public String toJsonString() {
|
public String toJsonString() {
|
||||||
StringBuffer strBuf = new StringBuffer();
|
JsonStringBuffer jsonBuf = new JsonStringBuffer(1);
|
||||||
|
appendTo(jsonBuf);
|
||||||
|
return jsonBuf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
strBuf.append("\t/// The boot-sequence configuration, contains the following items: \n");
|
public static void main(String[] pArgs) {
|
||||||
strBuf.append("\t/// * type : The type of the boot-sequence ('rainbow', 'knightrider', 'none') \n");
|
MiscConfig miscConfig = new MiscConfig();
|
||||||
strBuf.append("\t/// * duration_ms : The length of the boot-sequence [ms]\n");
|
|
||||||
|
|
||||||
String bootPreamble = mBootsequenceEnabled? "\t" : "//\t";
|
JsonStringBuffer jsonBuf = new JsonStringBuffer(1);
|
||||||
strBuf.append(bootPreamble).append("\"bootsequence\" :\n");
|
miscConfig.appendTo(jsonBuf);
|
||||||
strBuf.append(bootPreamble).append("{\n");
|
|
||||||
strBuf.append(bootPreamble).append(String.format(Locale.ROOT, "\t\"type\" : \"%s\",\n", mBootSequence.name()));
|
|
||||||
strBuf.append(bootPreamble).append(String.format(Locale.ROOT, "\t\"duration_ms\" : %d\n", mBootSequenceLength_ms));
|
|
||||||
strBuf.append(bootPreamble).append("},\n\n");
|
|
||||||
|
|
||||||
strBuf.append("\t/// The configuration for the frame-grabber, contains the following items: \n");
|
System.out.println(jsonBuf.toString());
|
||||||
strBuf.append("\t/// * width : The width of the grabbed frames [pixels]\n");
|
|
||||||
strBuf.append("\t/// * height : The height of the grabbed frames [pixels]\n");
|
|
||||||
strBuf.append("\t/// * frequency_Hz : The frequency of the frame grab [Hz]\n");
|
|
||||||
|
|
||||||
String grabPreamble = mFrameGrabberEnabled? "\t" : "//\t";
|
|
||||||
strBuf.append(grabPreamble).append("\"framegrabber\" :\n");
|
|
||||||
strBuf.append(grabPreamble).append("{\n");
|
|
||||||
strBuf.append(grabPreamble).append(String.format(Locale.ROOT, "\t\"width\" : %d,\n", mFrameGrabberWidth));
|
|
||||||
strBuf.append(grabPreamble).append(String.format(Locale.ROOT, "\t\"height\" : %d,\n", mFrameGrabberHeight));
|
|
||||||
strBuf.append(grabPreamble).append(String.format(Locale.ROOT, "\t\"frequency_Hz\" : %.1f\n", 1000.0/mFrameGrabberInterval_ms));
|
|
||||||
strBuf.append(grabPreamble).append("},\n\n");
|
|
||||||
|
|
||||||
strBuf.append("\t/// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields: \n");
|
|
||||||
strBuf.append("\t/// * xbmcAddress : The IP address of the XBMC-host\n");
|
|
||||||
strBuf.append("\t/// * xbmcTcpPort : The TCP-port of the XBMC-server\n");
|
|
||||||
strBuf.append("\t/// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback\n");
|
|
||||||
strBuf.append("\t/// * grabPictures : Flag indicating that the frame-grabber is on(true) during picture show\n");
|
|
||||||
strBuf.append("\t/// * grabAudio : Flag indicating that the frame-grabber is on(true) during audio playback\n");
|
|
||||||
strBuf.append("\t/// * grabMenu : Flag indicating that the frame-grabber is on(true) in the XBMC menu\n");
|
|
||||||
|
|
||||||
String xbmcPreamble = mXbmcCheckerEnabled? "\t" : "//\t";
|
|
||||||
strBuf.append(xbmcPreamble).append("\"xbmcVideoChecker\" :\n");
|
|
||||||
strBuf.append(xbmcPreamble).append("{\n");
|
|
||||||
strBuf.append(xbmcPreamble).append(String.format(Locale.ROOT, "\t\"xbmcAddress\" : \"%s\",\n", mXbmcAddress));
|
|
||||||
strBuf.append(xbmcPreamble).append(String.format(Locale.ROOT, "\t\"xbmcTcpPort\" : %d,\n", mXbmcTcpPort));
|
|
||||||
strBuf.append(xbmcPreamble).append(String.format(Locale.ROOT, "\t\"grabVideo\" : %s,\n", mVideoOn));
|
|
||||||
strBuf.append(xbmcPreamble).append(String.format(Locale.ROOT, "\t\"grabPictures\" : %s,\n", mPictureOn));
|
|
||||||
strBuf.append(xbmcPreamble).append(String.format(Locale.ROOT, "\t\"grabAudio\" : %s,\n", mAudioOn));
|
|
||||||
strBuf.append(xbmcPreamble).append(String.format(Locale.ROOT, "\t\"grabMenu\" : %s\n", mMenuOn));
|
|
||||||
strBuf.append(xbmcPreamble).append("},\n\n");
|
|
||||||
|
|
||||||
|
|
||||||
strBuf.append("\t/// The configuration of the Json server which enables the json remote interface\n");
|
|
||||||
strBuf.append("\t/// * port : Port at which the json server is started\n");
|
|
||||||
|
|
||||||
String jsonPreamble = mJsonInterfaceEnabled? "\t" : "//\t";
|
|
||||||
strBuf.append(jsonPreamble).append("\"jsonServer\" :\n");
|
|
||||||
strBuf.append(jsonPreamble).append("{\n");
|
|
||||||
strBuf.append(jsonPreamble).append(String.format(Locale.ROOT, "\t\"port\" : %d\n", mJsonPort));
|
|
||||||
strBuf.append(jsonPreamble).append("},\n\n");
|
|
||||||
|
|
||||||
|
|
||||||
strBuf.append("\t/// The configuration of the Proto server which enables the protobuffer remote interface\n");
|
|
||||||
strBuf.append("\t/// * port : Port at which the protobuffer server is started\n");
|
|
||||||
|
|
||||||
String protoPreamble = mProtoInterfaceEnabled? "\t" : "//\t";
|
|
||||||
strBuf.append(protoPreamble).append("\"protoServer\" :\n");
|
|
||||||
strBuf.append(protoPreamble).append("{\n");
|
|
||||||
strBuf.append(protoPreamble).append(String.format(Locale.ROOT, "\t\"port\" : %d\n", mProtoPort));
|
|
||||||
strBuf.append(protoPreamble).append("},\n\n");
|
|
||||||
|
|
||||||
|
|
||||||
strBuf.append("\t/// The configuration of the boblight server which enables the boblight remote interface\n");
|
|
||||||
strBuf.append("\t/// * port : Port at which the boblight server is started\n");
|
|
||||||
|
|
||||||
String bobligthPreamble = mBoblightInterfaceEnabled? "\t" : "//\t";
|
|
||||||
strBuf.append(bobligthPreamble).append("\"boblightServer\" :\n");
|
|
||||||
strBuf.append(bobligthPreamble).append("{\n");
|
|
||||||
strBuf.append(bobligthPreamble).append(String.format(Locale.ROOT, "\t\"port\" : %d\n", mBoblightPort));
|
|
||||||
strBuf.append(bobligthPreamble).append("},\n\n");
|
|
||||||
|
|
||||||
strBuf.append("\t\"end-of-json\" : \"end-of-json\"");
|
|
||||||
|
|
||||||
return strBuf.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,35 +7,35 @@ public class TransformConfig {
|
|||||||
public String mId = "default";
|
public String mId = "default";
|
||||||
|
|
||||||
/** The indices to which this transform applies */
|
/** The indices to which this transform applies */
|
||||||
public String mLedIndexString = "0-49";
|
public String mLedIndexString = "*";
|
||||||
|
|
||||||
/** The saturation gain (in HSV space) */
|
/** The saturation gain (in HSV space) */
|
||||||
public double mSaturationGain = 1.0;
|
public double mSaturationGain = 1.0;
|
||||||
/** The value gain (in HSV space) */
|
/** The value gain (in HSV space) */
|
||||||
public double mValueGain = 1.5;
|
public double mValueGain = 1.0;
|
||||||
|
|
||||||
/** The minimum required RED-value (in RGB space) */
|
/** The minimum required RED-value (in RGB space) */
|
||||||
public double mRedThreshold = 0.1;
|
public double mRedThreshold = 0.0;
|
||||||
/** The gamma-curve correct for the RED-value (in RGB space) */
|
/** The gamma-curve correct for the RED-value (in RGB space) */
|
||||||
public double mRedGamma = 2.0;
|
public double mRedGamma = 1.0;
|
||||||
/** The black-level of the RED-value (in RGB space) */
|
/** The black-level of the RED-value (in RGB space) */
|
||||||
public double mRedBlacklevel = 0.0;
|
public double mRedBlacklevel = 0.0;
|
||||||
/** The white-level of the RED-value (in RGB space) */
|
/** The white-level of the RED-value (in RGB space) */
|
||||||
public double mRedWhitelevel = 0.8;
|
public double mRedWhitelevel = 1.0;
|
||||||
|
|
||||||
/** The minimum required GREEN-value (in RGB space) */
|
/** The minimum required GREEN-value (in RGB space) */
|
||||||
public double mGreenThreshold = 0.1;
|
public double mGreenThreshold = 0.0;
|
||||||
/** The gamma-curve correct for the GREEN-value (in RGB space) */
|
/** The gamma-curve correct for the GREEN-value (in RGB space) */
|
||||||
public double mGreenGamma = 2.0;
|
public double mGreenGamma = 1.0;
|
||||||
/** The black-level of the GREEN-value (in RGB space) */
|
/** The black-level of the GREEN-value (in RGB space) */
|
||||||
public double mGreenBlacklevel = 0.0;
|
public double mGreenBlacklevel = 0.0;
|
||||||
/** The white-level of the GREEN-value (in RGB space) */
|
/** The white-level of the GREEN-value (in RGB space) */
|
||||||
public double mGreenWhitelevel = 1.0;
|
public double mGreenWhitelevel = 1.0;
|
||||||
|
|
||||||
/** The minimum required BLUE-value (in RGB space) */
|
/** The minimum required BLUE-value (in RGB space) */
|
||||||
public double mBlueThreshold = 0.1;
|
public double mBlueThreshold = 0.0;
|
||||||
/** The gamma-curve correct for the BLUE-value (in RGB space) */
|
/** The gamma-curve correct for the BLUE-value (in RGB space) */
|
||||||
public double mBlueGamma = 2.0;
|
public double mBlueGamma = 1.0;
|
||||||
/** The black-level of the BLUE-value (in RGB space) */
|
/** The black-level of the BLUE-value (in RGB space) */
|
||||||
public double mBlueBlacklevel = 0.0;
|
public double mBlueBlacklevel = 0.0;
|
||||||
/** The white-level of the BLUE-value (in RGB space) */
|
/** The white-level of the BLUE-value (in RGB space) */
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include "ColorTransformValues.h"
|
#include "ColorTransformValues.h"
|
||||||
|
|
||||||
/// Data parameter for a color
|
/// Data parameter for a color
|
||||||
typedef vlofgren::PODParameter<QColor> ColorParameter;
|
typedef vlofgren::PODParameter<std::vector<QColor>> ColorParameter;
|
||||||
|
|
||||||
/// Data parameter for an image
|
/// Data parameter for an image
|
||||||
typedef vlofgren::PODParameter<QImage> ImageParameter;
|
typedef vlofgren::PODParameter<QImage> ImageParameter;
|
||||||
@ -21,40 +21,54 @@ typedef vlofgren::PODParameter<ColorTransformValues> TransformParameter;
|
|||||||
|
|
||||||
namespace vlofgren {
|
namespace vlofgren {
|
||||||
///
|
///
|
||||||
/// Translates a string (as passed on the commandline) to a color
|
/// Translates a string (as passed on the commandline) to a vector of colors
|
||||||
///
|
///
|
||||||
/// @param[in] s The string (as passed on the commandline)
|
/// @param[in] s The string (as passed on the commandline)
|
||||||
///
|
///
|
||||||
/// @return The translated color
|
/// @return The translated colors
|
||||||
///
|
///
|
||||||
/// @throws Parameter::ParameterRejected If the string did not result in a color
|
/// @throws Parameter::ParameterRejected If the string did not result in a color
|
||||||
///
|
///
|
||||||
template<>
|
template<>
|
||||||
QColor ColorParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
|
std::vector<QColor> ColorParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
|
||||||
{
|
{
|
||||||
// Check if we can create the color by name
|
// Check if we can create the color by name
|
||||||
QColor color(s.c_str());
|
QColor color(s.c_str());
|
||||||
if (color.isValid())
|
if (color.isValid())
|
||||||
{
|
{
|
||||||
return color;
|
return std::vector<QColor>{color};
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we can create the color by hex RRGGBB value
|
// check if we can create the color by hex RRGGBB value
|
||||||
if (s.length() == 6 && isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) && isxdigit(s[5]))
|
if (s.length() >= 6u && (s.length()%6) == 0u && std::count_if(s.begin(), s.end(), isxdigit) == s.length())
|
||||||
{
|
{
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
std::vector<QColor> colors;
|
||||||
|
|
||||||
|
for (size_t j = 0; j < s.length()/6; ++j)
|
||||||
|
{
|
||||||
int rgb[3];
|
int rgb[3];
|
||||||
for (int i = 0; i < 3 && ok; ++i)
|
for (int i = 0; i < 3 && ok; ++i)
|
||||||
{
|
{
|
||||||
QString colorComponent(s.substr(2*i, 2).c_str());
|
QString colorComponent(s.substr(6*j+2*i, 2).c_str());
|
||||||
rgb[i] = colorComponent.toInt(&ok, 16);
|
rgb[i] = colorComponent.toInt(&ok, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
color.setRgb(rgb[0], rgb[1], rgb[2]);
|
||||||
|
colors.push_back(color);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check if all components parsed succesfully
|
// check if all components parsed succesfully
|
||||||
if (ok)
|
if (ok)
|
||||||
{
|
{
|
||||||
color.setRgb(rgb[0], rgb[1], rgb[2]);
|
return colors;
|
||||||
return color;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +79,7 @@ namespace vlofgren {
|
|||||||
}
|
}
|
||||||
throw Parameter::ParameterRejected(errorMessage.str());
|
throw Parameter::ParameterRejected(errorMessage.str());
|
||||||
|
|
||||||
return color;
|
return std::vector<QColor>{color};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -39,18 +39,21 @@ JsonConnection::~JsonConnection()
|
|||||||
_socket.close();
|
_socket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonConnection::setColor(QColor color, int priority, int duration)
|
void JsonConnection::setColor(std::vector<QColor> colors, int priority, int duration)
|
||||||
{
|
{
|
||||||
std::cout << "Set color to " << color.red() << " " << color.green() << " " << color.blue() << std::endl;
|
std::cout << "Set color to " << colors[0].red() << " " << colors[0].green() << " " << colors[0].blue() << (colors.size() > 1 ? " + ..." : "") << std::endl;
|
||||||
|
|
||||||
// create command
|
// create command
|
||||||
Json::Value command;
|
Json::Value command;
|
||||||
command["command"] = "color";
|
command["command"] = "color";
|
||||||
command["priority"] = priority;
|
command["priority"] = priority;
|
||||||
Json::Value & rgbValue = command["color"];
|
Json::Value & rgbValue = command["color"];
|
||||||
rgbValue[0] = color.red();
|
for (const QColor & color : colors)
|
||||||
rgbValue[1] = color.green();
|
{
|
||||||
rgbValue[2] = color.blue();
|
rgbValue.append(color.red());
|
||||||
|
rgbValue.append(color.green());
|
||||||
|
rgbValue.append(color.blue());
|
||||||
|
}
|
||||||
if (duration > 0)
|
if (duration > 0)
|
||||||
{
|
{
|
||||||
command["duration"] = duration;
|
command["duration"] = duration;
|
||||||
@ -102,6 +105,35 @@ void JsonConnection::setImage(QImage image, int priority, int duration)
|
|||||||
parseReply(reply);
|
parseReply(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonConnection::setEffect(const std::string &effectName, const std::string & effectArgs, int priority, int duration)
|
||||||
|
{
|
||||||
|
std::cout << "Start effect " << effectName << std::endl;
|
||||||
|
|
||||||
|
// create command
|
||||||
|
Json::Value command;
|
||||||
|
command["command"] = "effect";
|
||||||
|
command["priority"] = priority;
|
||||||
|
Json::Value & effect = command["effect"];
|
||||||
|
effect["name"] = effectName;
|
||||||
|
if (effectArgs.size() > 0)
|
||||||
|
{
|
||||||
|
Json::Reader reader;
|
||||||
|
if (!reader.parse(effectArgs, effect["args"], false))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Error in effect arguments: " + reader.getFormattedErrorMessages());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (duration > 0)
|
||||||
|
{
|
||||||
|
command["duration"] = duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send command message
|
||||||
|
Json::Value reply = sendMessage(command);
|
||||||
|
|
||||||
|
// parse reply message
|
||||||
|
parseReply(reply);
|
||||||
|
}
|
||||||
|
|
||||||
QString JsonConnection::getServerInfo()
|
QString JsonConnection::getServerInfo()
|
||||||
{
|
{
|
||||||
|
@ -41,7 +41,7 @@ public:
|
|||||||
/// @param priority The priority
|
/// @param priority The priority
|
||||||
/// @param duration The duration in milliseconds
|
/// @param duration The duration in milliseconds
|
||||||
///
|
///
|
||||||
void setColor(QColor color, int priority, int duration);
|
void setColor(std::vector<QColor> color, int priority, int duration);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Set the leds according to the given image (assume the image is stretched to the display size)
|
/// Set the leds according to the given image (assume the image is stretched to the display size)
|
||||||
@ -52,6 +52,16 @@ public:
|
|||||||
///
|
///
|
||||||
void setImage(QImage image, int priority, int duration);
|
void setImage(QImage image, int priority, int duration);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Start the given effect
|
||||||
|
///
|
||||||
|
/// @param effect The name of the effect
|
||||||
|
/// @param effectArgs The arguments to use instead of the default ones
|
||||||
|
/// @param priority The priority
|
||||||
|
/// @param duration The duration in milliseconds
|
||||||
|
///
|
||||||
|
void setEffect(const std::string & effectName, const std::string &effectArgs, int priority, int duration);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Retrieve a list of all occupied priority channels
|
/// Retrieve a list of all occupied priority channels
|
||||||
///
|
///
|
||||||
|
@ -40,8 +40,10 @@ int main(int argc, char * argv[])
|
|||||||
StringParameter & argAddress = parameters.add<StringParameter> ('a', "address" , QString("Set the address of the hyperion server [default: %1]").arg(defaultServerAddress).toAscii().constData());
|
StringParameter & argAddress = parameters.add<StringParameter> ('a', "address" , QString("Set the address of the hyperion server [default: %1]").arg(defaultServerAddress).toAscii().constData());
|
||||||
IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority" , QString("Use to the provided priority channel (the lower the number, the higher the priority) [default: %1]").arg(defaultPriority).toAscii().constData());
|
IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority" , QString("Use to the provided priority channel (the lower the number, the higher the priority) [default: %1]").arg(defaultPriority).toAscii().constData());
|
||||||
IntParameter & argDuration = parameters.add<IntParameter> ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]");
|
IntParameter & argDuration = parameters.add<IntParameter> ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]");
|
||||||
ColorParameter & argColor = parameters.add<ColorParameter> ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name)");
|
ColorParameter & argColor = parameters.add<ColorParameter> ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name. The color may be repeated multiple time like: RRGGBBRRGGBB)");
|
||||||
ImageParameter & argImage = parameters.add<ImageParameter> ('i', "image" , "Set the leds to the colors according to the given image file");
|
ImageParameter & argImage = parameters.add<ImageParameter> ('i', "image" , "Set the leds to the colors according to the given image file");
|
||||||
|
StringParameter & argEffect = parameters.add<StringParameter> ('e', "effect" , "Enable the effect with the given name");
|
||||||
|
StringParameter & argEffectArgs = parameters.add<StringParameter> (0x0, "effectArgs", "Arguments to use in combination with the specified effect. Should be a Json object string.");
|
||||||
SwitchParameter<> & argServerInfo = parameters.add<SwitchParameter<> >('l', "list" , "List server info");
|
SwitchParameter<> & argServerInfo = parameters.add<SwitchParameter<> >('l', "list" , "List server info");
|
||||||
SwitchParameter<> & argClear = parameters.add<SwitchParameter<> >('x', "clear" , "Clear data for the priority channel provided by the -p option");
|
SwitchParameter<> & argClear = parameters.add<SwitchParameter<> >('x', "clear" , "Clear data for the priority channel provided by the -p option");
|
||||||
SwitchParameter<> & argClearAll = parameters.add<SwitchParameter<> >(0x0, "clearall" , "Clear data for all active priority channels");
|
SwitchParameter<> & argClearAll = parameters.add<SwitchParameter<> >(0x0, "clearall" , "Clear data for all active priority channels");
|
||||||
@ -59,6 +61,7 @@ int main(int argc, char * argv[])
|
|||||||
argAddress.setDefault(defaultServerAddress.toStdString());
|
argAddress.setDefault(defaultServerAddress.toStdString());
|
||||||
argPriority.setDefault(defaultPriority);
|
argPriority.setDefault(defaultPriority);
|
||||||
argDuration.setDefault(-1);
|
argDuration.setDefault(-1);
|
||||||
|
argEffectArgs.setDefault("");
|
||||||
|
|
||||||
// parse all options
|
// parse all options
|
||||||
optionParser.parse(argc, const_cast<const char **>(argv));
|
optionParser.parse(argc, const_cast<const char **>(argv));
|
||||||
@ -74,12 +77,13 @@ int main(int argc, char * argv[])
|
|||||||
bool colorTransform = argSaturation.isSet() || argValue.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet();
|
bool colorTransform = argSaturation.isSet() || argValue.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet();
|
||||||
|
|
||||||
// check that exactly one command was given
|
// check that exactly one command was given
|
||||||
int commandCount = count({argColor.isSet(), argImage.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform});
|
int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform});
|
||||||
if (commandCount != 1)
|
if (commandCount != 1)
|
||||||
{
|
{
|
||||||
std::cerr << (commandCount == 0 ? "No command found." : "Multiple commands found.") << " Provide exactly one of the following options:" << std::endl;
|
std::cerr << (commandCount == 0 ? "No command found." : "Multiple commands found.") << " Provide exactly one of the following options:" << std::endl;
|
||||||
std::cerr << " " << argColor.usageLine() << std::endl;
|
std::cerr << " " << argColor.usageLine() << std::endl;
|
||||||
std::cerr << " " << argImage.usageLine() << std::endl;
|
std::cerr << " " << argImage.usageLine() << std::endl;
|
||||||
|
std::cerr << " " << argEffect.usageLine() << std::endl;
|
||||||
std::cerr << " " << argServerInfo.usageLine() << std::endl;
|
std::cerr << " " << argServerInfo.usageLine() << std::endl;
|
||||||
std::cerr << " " << argClear.usageLine() << std::endl;
|
std::cerr << " " << argClear.usageLine() << std::endl;
|
||||||
std::cerr << " " << argClearAll.usageLine() << std::endl;
|
std::cerr << " " << argClearAll.usageLine() << std::endl;
|
||||||
@ -106,6 +110,10 @@ int main(int argc, char * argv[])
|
|||||||
{
|
{
|
||||||
connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue());
|
connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue());
|
||||||
}
|
}
|
||||||
|
else if (argEffect.isSet())
|
||||||
|
{
|
||||||
|
connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue());
|
||||||
|
}
|
||||||
else if (argServerInfo.isSet())
|
else if (argServerInfo.isSet())
|
||||||
{
|
{
|
||||||
QString info = connection.getServerInfo();
|
QString info = connection.getServerInfo();
|
||||||
|
@ -3,9 +3,9 @@ add_executable(hyperiond
|
|||||||
hyperiond.cpp)
|
hyperiond.cpp)
|
||||||
|
|
||||||
target_link_libraries(hyperiond
|
target_link_libraries(hyperiond
|
||||||
bootsequence
|
|
||||||
hyperion
|
hyperion
|
||||||
xbmcvideochecker
|
xbmcvideochecker
|
||||||
|
effectengine
|
||||||
jsonserver
|
jsonserver
|
||||||
protoserver
|
protoserver
|
||||||
boblightserver)
|
boblightserver)
|
||||||
|
@ -15,9 +15,6 @@
|
|||||||
// Hyperion includes
|
// Hyperion includes
|
||||||
#include <hyperion/Hyperion.h>
|
#include <hyperion/Hyperion.h>
|
||||||
|
|
||||||
// Bootsequence includes
|
|
||||||
#include <bootsequence/BootSequenceFactory.h>
|
|
||||||
|
|
||||||
#ifdef ENABLE_DISPMANX
|
#ifdef ENABLE_DISPMANX
|
||||||
// Dispmanx grabber includes
|
// Dispmanx grabber includes
|
||||||
#include <dispmanx-grabber/DispmanxWrapper.h>
|
#include <dispmanx-grabber/DispmanxWrapper.h>
|
||||||
@ -26,6 +23,9 @@
|
|||||||
// XBMC Video checker includes
|
// XBMC Video checker includes
|
||||||
#include <xbmcvideochecker/XBMCVideoChecker.h>
|
#include <xbmcvideochecker/XBMCVideoChecker.h>
|
||||||
|
|
||||||
|
// Effect engine includes
|
||||||
|
#include <effectengine/EffectEngine.h>
|
||||||
|
|
||||||
// JsonServer includes
|
// JsonServer includes
|
||||||
#include <jsonserver/JsonServer.h>
|
#include <jsonserver/JsonServer.h>
|
||||||
|
|
||||||
@ -93,15 +93,25 @@ int main(int argc, char** argv)
|
|||||||
std::cout << "Hyperion created and initialised" << std::endl;
|
std::cout << "Hyperion created and initialised" << std::endl;
|
||||||
|
|
||||||
// create boot sequence if the configuration is present
|
// create boot sequence if the configuration is present
|
||||||
BootSequence * bootSequence = nullptr;
|
|
||||||
if (config.isMember("bootsequence"))
|
if (config.isMember("bootsequence"))
|
||||||
{
|
{
|
||||||
bootSequence = BootSequenceFactory::createBootSequence(&hyperion, config["bootsequence"]);
|
const Json::Value effectConfig = config["bootsequence"];
|
||||||
|
|
||||||
if (bootSequence != nullptr)
|
// Get the parameters for the bootsequence
|
||||||
|
const std::string effectName = effectConfig["effect"].asString();
|
||||||
|
const unsigned duration_ms = effectConfig["duration_ms"].asUInt();
|
||||||
|
const int priority = 0;
|
||||||
|
|
||||||
|
// int retVal = -1;
|
||||||
|
// QMetaObject::invokeMethod(hyperion, "setEffect", Q_RETURN_ARG(int, retVal), Q_ARG(std::string, effectName), Q_ARG(Json::Value, Json::Value()), Q_ARG(int, priority), Q_ARG(int, duration_ms));
|
||||||
|
// if (retVal == 0)
|
||||||
|
if (hyperion.setEffect(effectName, priority, duration_ms) == 0)
|
||||||
{
|
{
|
||||||
bootSequence->start();
|
std::cout << "Boot sequence(" << effectName << ") created and started" << std::endl;
|
||||||
std::cout << "Boot sequence created and started" << std::endl;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "Failed to start boot sequence: " << effectName << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,13 +192,13 @@ int main(int argc, char** argv)
|
|||||||
std::cout << "Application closed with code " << rc << std::endl;
|
std::cout << "Application closed with code " << rc << std::endl;
|
||||||
|
|
||||||
// Delete all component
|
// Delete all component
|
||||||
delete bootSequence;
|
|
||||||
#ifdef ENABLE_DISPMANX
|
#ifdef ENABLE_DISPMANX
|
||||||
delete dispmanx;
|
delete dispmanx;
|
||||||
#endif
|
#endif
|
||||||
delete xbmcVideoChecker;
|
delete xbmcVideoChecker;
|
||||||
delete jsonServer;
|
delete jsonServer;
|
||||||
delete protoServer;
|
delete protoServer;
|
||||||
|
delete boblightServer;
|
||||||
|
|
||||||
// leave application
|
// leave application
|
||||||
return rc;
|
return rc;
|
||||||
|
Loading…
Reference in New Issue
Block a user