mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Handle defined exits from python scripts
This commit is contained in:
parent
07bf243a44
commit
f808b43573
@ -1,5 +1,3 @@
|
|||||||
# Two projectiles are sent from random positions and collide with each other
|
|
||||||
# Template from https://github.com/nickpesce/lit/blob/master/lit/effects/collision.py
|
|
||||||
import hyperion, time, colorsys, random
|
import hyperion, time, colorsys, random
|
||||||
|
|
||||||
# Get parameters
|
# Get parameters
|
||||||
@ -7,6 +5,11 @@ sleepTime = max(0.02, float(hyperion.args.get('speed', 100))/1000.0)
|
|||||||
trailLength = max(3, int(hyperion.args.get('trailLength', 5)))
|
trailLength = max(3, int(hyperion.args.get('trailLength', 5)))
|
||||||
explodeRadius = int(hyperion.args.get('explodeRadius', 8))
|
explodeRadius = int(hyperion.args.get('explodeRadius', 8))
|
||||||
|
|
||||||
|
# Ensure that the range for pixel indices stays within bounds
|
||||||
|
maxPixelIndex = hyperion.ledCount - 1
|
||||||
|
if trailLength > maxPixelIndex or explodeRadius > maxPixelIndex:
|
||||||
|
exit(f"Error: Color length ({trailLength}) and detonation range ({explodeRadius}) must be less than number of LEDs configured ({hyperion.ledCount})")
|
||||||
|
|
||||||
# Create additional variables
|
# Create additional variables
|
||||||
increment = None
|
increment = None
|
||||||
projectiles = []
|
projectiles = []
|
||||||
@ -18,38 +21,55 @@ for i in range(hyperion.ledCount):
|
|||||||
|
|
||||||
# Start the write data loop
|
# Start the write data loop
|
||||||
while not hyperion.abort():
|
while not hyperion.abort():
|
||||||
if (len(projectiles) != 2):
|
if len(projectiles) != 2:
|
||||||
projectiles = [ [0, 1, random.uniform(0.0, 1.0)], [hyperion.ledCount-1, -1, random.uniform(0.0, 1.0)] ]
|
projectiles = [
|
||||||
|
[0, 1, random.uniform(0.0, 1.0)], # Start positions of projectiles
|
||||||
|
[hyperion.ledCount-1, -1, random.uniform(0.0, 1.0)]
|
||||||
|
]
|
||||||
increment = -random.randint(0, hyperion.ledCount-1) if random.choice([True, False]) else random.randint(0, hyperion.ledCount-1)
|
increment = -random.randint(0, hyperion.ledCount-1) if random.choice([True, False]) else random.randint(0, hyperion.ledCount-1)
|
||||||
|
|
||||||
|
# Backup the LED data
|
||||||
ledDataBuf = ledData[:]
|
ledDataBuf = ledData[:]
|
||||||
for i, v in enumerate(projectiles):
|
for i, v in enumerate(projectiles):
|
||||||
projectiles[i][0] = projectiles[i][0]+projectiles[i][1]
|
# Update projectile positions
|
||||||
|
projectiles[i][0] = projectiles[i][0] + projectiles[i][1]
|
||||||
|
|
||||||
for t in range(0, trailLength):
|
for t in range(0, trailLength):
|
||||||
pixel = v[0] - v[1]*t
|
# Calculate pixel index for the trail
|
||||||
if pixel + 2 < 0:
|
pixel = v[0] - v[1] * t
|
||||||
|
if pixel < 0:
|
||||||
pixel += hyperion.ledCount
|
pixel += hyperion.ledCount
|
||||||
if pixel + 2 > hyperion.ledCount-1:
|
if pixel >= hyperion.ledCount:
|
||||||
pixel -= hyperion.ledCount-1
|
pixel -= hyperion.ledCount
|
||||||
rgb = colorsys.hsv_to_rgb(v[2], 1, (trailLength - 1.0*t)/trailLength)
|
|
||||||
ledDataBuf[3*pixel ] = int(255*rgb[0])
|
# Make sure pixel is within bounds
|
||||||
ledDataBuf[3*pixel + 1] = int(255*rgb[1])
|
if pixel < 0 or pixel >= hyperion.ledCount:
|
||||||
ledDataBuf[3*pixel + 2] = int(255*rgb[2])
|
continue
|
||||||
|
|
||||||
|
rgb = colorsys.hsv_to_rgb(v[2], 1, (trailLength - 1.0 * t) / trailLength)
|
||||||
|
ledDataBuf[3*pixel] = int(255 * rgb[0])
|
||||||
|
ledDataBuf[3*pixel + 1] = int(255 * rgb[1])
|
||||||
|
ledDataBuf[3*pixel + 2] = int(255 * rgb[2])
|
||||||
|
|
||||||
hyperion.setColor(ledDataBuf[-increment:] + ledDataBuf[:-increment])
|
hyperion.setColor(ledDataBuf[-increment:] + ledDataBuf[:-increment])
|
||||||
|
|
||||||
|
# Check for collision and handle explosion
|
||||||
for i1, p1 in enumerate(projectiles):
|
for i1, p1 in enumerate(projectiles):
|
||||||
for i2, p2 in enumerate(projectiles):
|
for i2, p2 in enumerate(projectiles):
|
||||||
if (p1 is not p2):
|
if p1 is not p2:
|
||||||
prev1 = p1[0] - p1[1]
|
prev1 = p1[0] - p1[1]
|
||||||
prev2 = p2[0] - p2[1]
|
prev2 = p2[0] - p2[1]
|
||||||
if (prev1 - prev2 < 0) != (p1[0] - p2[0] < 0):
|
if (prev1 - prev2 < 0) != (p1[0] - p2[0] < 0):
|
||||||
for d in range(0, explodeRadius):
|
for d in range(0, explodeRadius):
|
||||||
for pixel in range(p1[0] - d, p1[0] + d):
|
for pixel in range(p1[0] - d, p1[0] + d):
|
||||||
|
# Check if pixel is out of bounds
|
||||||
|
if pixel < 0 or pixel >= hyperion.ledCount:
|
||||||
|
continue
|
||||||
|
|
||||||
rgb = colorsys.hsv_to_rgb(random.choice([p1[2], p2[2]]), 1, (1.0 * explodeRadius - d) / explodeRadius)
|
rgb = colorsys.hsv_to_rgb(random.choice([p1[2], p2[2]]), 1, (1.0 * explodeRadius - d) / explodeRadius)
|
||||||
ledDataBuf[3*pixel ] = int(255*rgb[0])
|
ledDataBuf[3 * pixel] = int(255 * rgb[0])
|
||||||
ledDataBuf[3*pixel + 1] = int(255*rgb[1])
|
ledDataBuf[3 * pixel + 1] = int(255 * rgb[1])
|
||||||
ledDataBuf[3*pixel + 2] = int(255*rgb[2])
|
ledDataBuf[3 * pixel + 2] = int(255 * rgb[2])
|
||||||
|
|
||||||
hyperion.setColor(ledDataBuf[-increment:] + ledDataBuf[:-increment])
|
hyperion.setColor(ledDataBuf[-increment:] + ledDataBuf[:-increment])
|
||||||
time.sleep(sleepTime)
|
time.sleep(sleepTime)
|
||||||
|
@ -102,16 +102,66 @@ void PythonProgram::execute(const QByteArray& python_code)
|
|||||||
{
|
{
|
||||||
if (PyErr_Occurred())
|
if (PyErr_Occurred())
|
||||||
{
|
{
|
||||||
Error(_log, "###### PYTHON EXCEPTION ######");
|
|
||||||
Error(_log, "## In effect '%s'", QSTRING_CSTR(_name));
|
|
||||||
|
|
||||||
PyObject* errorType = NULL, * errorValue = NULL, * errorTraceback = NULL;
|
PyObject* errorType = NULL, * errorValue = NULL, * errorTraceback = NULL;
|
||||||
|
|
||||||
PyErr_Fetch(&errorType, &errorValue, &errorTraceback);
|
PyErr_Fetch(&errorType, &errorValue, &errorTraceback);
|
||||||
PyErr_NormalizeException(&errorType, &errorValue, &errorTraceback);
|
PyErr_NormalizeException(&errorType, &errorValue, &errorTraceback);
|
||||||
|
|
||||||
|
// Check if the exception is a SystemExit
|
||||||
|
PyObject* systemExitType = PyExc_SystemExit;
|
||||||
|
bool isSystemExit = PyObject_IsInstance(errorValue, systemExitType);
|
||||||
|
|
||||||
|
if (isSystemExit)
|
||||||
|
{
|
||||||
|
// Extract the exit argument
|
||||||
|
PyObject* exitArg = PyObject_GetAttrString(errorValue, "code");
|
||||||
|
|
||||||
|
if (exitArg)
|
||||||
|
{
|
||||||
|
QString logErrorText;
|
||||||
|
if (PyTuple_Check(exitArg)) {
|
||||||
|
PyObject* errorMessage = PyTuple_GetItem(exitArg, 0); // Borrowed reference
|
||||||
|
PyObject* exitCode = PyTuple_GetItem(exitArg, 1); // Borrowed reference
|
||||||
|
|
||||||
|
if (exitCode && PyLong_Check(exitCode))
|
||||||
|
{
|
||||||
|
logErrorText = QString("[%1]: ").arg(PyLong_AsLong(exitCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMessage && PyUnicode_Check(errorMessage)) {
|
||||||
|
logErrorText.append(PyUnicode_AsUTF8(errorMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PyUnicode_Check(exitArg)) {
|
||||||
|
// If the code is just a string, treat it as an error message
|
||||||
|
logErrorText.append(PyUnicode_AsUTF8(exitArg));
|
||||||
|
}
|
||||||
|
else if (PyLong_Check(exitArg)) {
|
||||||
|
// If the code is just an integer, treat it as an exit code
|
||||||
|
logErrorText = QString("[%1]").arg(PyLong_AsLong(exitArg));
|
||||||
|
}
|
||||||
|
|
||||||
|
Error(_log, "Effect '%s' failed with error %s", QSTRING_CSTR(_name), QSTRING_CSTR(logErrorText));
|
||||||
|
|
||||||
|
Py_DECREF(exitArg); // Release the reference
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug(_log, "No 'code' attribute found on SystemExit exception.");
|
||||||
|
}
|
||||||
|
// Clear the error so it won't propagate
|
||||||
|
PyErr_Clear();
|
||||||
|
|
||||||
|
Py_DECREF(systemExitType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Py_DECREF(systemExitType);
|
||||||
|
|
||||||
if (errorValue)
|
if (errorValue)
|
||||||
{
|
{
|
||||||
|
Error(_log, "###### PYTHON EXCEPTION ######");
|
||||||
|
Error(_log, "## In effect '%s'", QSTRING_CSTR(_name));
|
||||||
|
|
||||||
QString message;
|
QString message;
|
||||||
if (PyObject_HasAttrString(errorValue, "__class__"))
|
if (PyObject_HasAttrString(errorValue, "__class__"))
|
||||||
{
|
{
|
||||||
@ -166,6 +216,8 @@ void PythonProgram::execute(const QByteArray& python_code)
|
|||||||
}
|
}
|
||||||
Error(_log, "###### EXCEPTION END ######");
|
Error(_log, "###### EXCEPTION END ######");
|
||||||
}
|
}
|
||||||
|
// Clear the error so it won't propagate
|
||||||
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user