From 5820e07ccfbf36f171cc2261b1daf646fea34836 Mon Sep 17 00:00:00 2001 From: vanous Date: Thu, 28 Dec 2023 14:34:55 +0100 Subject: [PATCH 1/4] Make setting universes more understandable --- __init__.py | 31 ++++++++++++++++++++----------- data.py | 6 +++--- fixture.py | 8 +++++++- panels/dmx.py | 39 +++++++++++++++++++++++++++++++++++---- universe.py | 2 +- 5 files changed, 66 insertions(+), 20 deletions(-) diff --git a/__init__.py b/__init__.py index 8a5b7759..5a4c37bc 100644 --- a/__init__.py +++ b/__init__.py @@ -270,16 +270,25 @@ def prepare_empty_buffer(self, context): # Clear the buffer on change of every protocol DMX_Data.prepare_empty_buffer() - selected_live_dmx_source : EnumProperty( - name = "DMX Source", - description="The network protocol for which to show live DMX values", - default = "BLENDERDMX", - items = network_options_list, - update = prepare_empty_buffer, - ) - selected_live_dmx_universe: IntProperty( - min = 0, - update = prepare_empty_buffer, + + def get_dmx_universes(self, context): + #print(self, context) + data = [] + for universe in self.universes: + data.append((str(universe.id), universe.name, str(universe.input), "", universe.id)) + return data + + def get_selected_live_dmx_universe(self): + for universe in self.universes: + selected_universe = universe + if self.selected_live_dmx == str(universe.id): + break + return selected_universe + + selected_live_dmx: EnumProperty( + name = "Universe", + description="", + items = get_dmx_universes ) dmx_values: CollectionProperty( @@ -1319,7 +1328,7 @@ def updatePreviewVolume(self): def addUniverse(self): id = len(self.universes) - DMX_Universe.add(self, id, "DMX %d"%id) + DMX_Universe.add(self, id, "Universe %d"%id) def removeUniverse(self, i): DMX_Universe.remove(self, i) diff --git a/data.py b/data.py index d325152f..f44eb9d9 100644 --- a/data.py +++ b/data.py @@ -95,10 +95,9 @@ def set(universe, addr, val): if addr > 511: return - if DMX_Data._dmx is not None: dmx = DMX_Data._dmx - if dmx.selected_live_dmx_source == "BLENDERDMX": + if dmx.get_selected_live_dmx_universe().input == "BLENDERDMX": dmx = bpy.context.scene.dmx dmx.dmx_values[addr-1].channel=val DMX_Data._universes[universe][addr-1] = val @@ -132,7 +131,8 @@ def set_universe(universe, data, source): if DMX_Data._dmx is not None: dmx = DMX_Data._dmx - if dmx.selected_live_dmx_source == source and dmx.selected_live_dmx_universe == universe: + selected_live_dmx_universe = dmx.get_selected_live_dmx_universe() + if selected_live_dmx_universe.input == source and selected_live_dmx_universe.id == universe: if DMX_Data._last_updated is None or (time.time() - DMX_Data._last_updated > 0.8 and changed): # We limit update by time, too fast updates were troubling Blender's UI for idx, val in enumerate(data): diff --git a/fixture.py b/fixture.py index 67a04bda..a350546b 100644 --- a/fixture.py +++ b/fixture.py @@ -123,12 +123,18 @@ class DMX_Fixture(PropertyGroup): type = DMX_Fixture_Channel ) + def ensure_universe_exists(self, context): + dmx = bpy.context.scene.dmx + dmx.ensureUniverseExists(self.universe) + universe : IntProperty( name = "Fixture > Universe", description="Fixture DMX Universe", default = 0, min = 0, - max = 511) + max = 511, + update = ensure_universe_exists + ) address : IntProperty( name = "Fixture > Address", diff --git a/panels/dmx.py b/panels/dmx.py index 40894704..76845a7d 100644 --- a/panels/dmx.py +++ b/panels/dmx.py @@ -129,8 +129,13 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn ob = data icon = "FILE_VOLUME" if self.layout_type in {'DEFAULT', 'COMPACT'}: - layout.prop(item, "name", text="", emboss=False, icon=icon) - layout.label(text=item.input) + col = layout.column() + col.label(text=f"{item.id}", icon=icon) + col.ui_units_x = 2 + col = layout.column() + col.prop(item, "name", text="", emboss=False) + col = layout.column() + col.label(text=item.input) elif self.layout_type in {'GRID'}: layout.alignment = 'CENTER' layout.label(text=str(item.id), icon=icon) @@ -267,6 +272,11 @@ class DMX_PT_DMX_ArtNet(Panel): def draw(self, context): layout = self.layout dmx = context.scene.dmx + + artnet_universes = [] + for universe in dmx.universes: + if universe.input == "ARTNET": + artnet_universes.append(universe) row = layout.row() row.prop(dmx, "artnet_ipaddr", text="IPv4") @@ -274,6 +284,9 @@ def draw(self, context): row = layout.row() row.prop(dmx, "artnet_enabled") + row.enabled = len(artnet_universes)>0 + row = layout.row() + row.label(text=f"Art-Net set for {len(artnet_universes)} universe(s)") layout.label(text='Status: ' + layout.enum_item_name(dmx, 'artnet_status', dmx.artnet_status)) class DMX_PT_DMX_sACN(Panel): @@ -290,8 +303,18 @@ def draw(self, context): layout = self.layout dmx = context.scene.dmx + sacn_universes = [] + for index, universe in enumerate(dmx.universes): + if index == 0: # invalid for sACN + continue + if universe.input == "sACN": + sacn_universes.append(universe) + row = layout.row() row.prop(dmx, "sacn_enabled") + row.enabled = len(sacn_universes)>0 + row = layout.row() + row.label(text=f"sACN set for {len(sacn_universes)} universe(s)") layout.label(text='Status: ' + dmx.sacn_status) class DMX_UL_LiveDMX_items(UIList): @@ -316,10 +339,18 @@ class DMX_PT_DMX_LiveDMX(Panel): def draw(self, context): layout = self.layout dmx = context.scene.dmx + selected_universe = dmx.get_selected_live_dmx_universe() row = layout.row() - row.prop(dmx, "selected_live_dmx_source", text="Source") - row.prop(dmx, "selected_live_dmx_universe", text="Universe") + row.prop(dmx, "selected_live_dmx", text="Source") + row = layout.row() + col = row.column() + col.label(text=f"{selected_universe.id}") + col.ui_units_x = 2 + col = row.column() + row.label(text=f"{selected_universe.name}") + col = row.column() + row.label(text=f"{selected_universe.input}") layout.template_list("DMX_UL_LiveDMX_items", "", dmx, "dmx_values", dmx, "dmx_value_index", type='GRID') # Panel # diff --git a/universe.py b/universe.py index 71300671..be3d7710 100644 --- a/universe.py +++ b/universe.py @@ -29,7 +29,7 @@ class DMX_Universe(PropertyGroup): name: StringProperty ( name = "Name", description = "Name of the universe", - default = "DMX 0" + default = "Universe 0" ) input: EnumProperty ( From ced85f12a6e2ea410786c5107c860023d10e6c46 Mon Sep 17 00:00:00 2001 From: vanous Date: Thu, 28 Dec 2023 16:01:56 +0100 Subject: [PATCH 2/4] Add another error handling to Art-Net --- artnet.py | 29 ++++++++++++++++++++--------- data.py | 1 - panels/dmx.py | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/artnet.py b/artnet.py index a702edec..e4d1a130 100644 --- a/artnet.py +++ b/artnet.py @@ -56,8 +56,14 @@ def __init__(self, ip_addr, *args, **kwargs): self._socket = socket(AF_INET, SOCK_DGRAM) self._socket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) self._socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) - self._socket.bind((ip_addr, ARTNET_PORT)) - self._socket.settimeout(20) + try: + self._socket.bind((ip_addr, ARTNET_PORT)) + except OSError as e: + print(e) + self._dmx.artnet_status = "socket_error" + raise ValueError("Socket opening error") + + #self._socket.settimeout(30) self._stopped = False def stop(self): @@ -65,9 +71,9 @@ def stop(self): self._socket.shutdown(SHUT_RDWR) except Exception as e: print("Error while stopping", e) - raise ValueError("Socket closing error") - finally: self._stopped = True + raise ValueError("Socket closing error") + self._stopped = True def run(self): @@ -77,7 +83,6 @@ def run(self): data = self._socket.recv(1024) except Exception as e: print(e) - print("data", data) if len(data) < 8: continue if struct.unpack("!8s", data[:8])[0] != ArtnetPacket.ARTNET_HEADER: @@ -93,6 +98,7 @@ def run(self): self._dmx.artnet_status = "socket_close" self._socket.close() self._dmx.artnet_status = "offline" + self._stopped = True def handle_ArtPoll(self): """ArtPoll is a message to find out which other ArtNet devices are in the network. @@ -125,14 +131,14 @@ def build_ArtPollReply(self): else: ip = "0.0.0.0" ip = [int(i) for i in ip.split(".")] - content += [i.to_bytes() for i in ip] + content += [struct.pack('B', i) for i in ip] # Port content.append(struct.pack(" Nope -> 0 @@ -169,7 +175,11 @@ def enable(): print("\t%s:%s" % (dmx.artnet_ipaddr, ARTNET_PORT)) dmx.artnet_status = "socket_open" - DMX_ArtNet._thread = DMX_ArtNet(dmx.artnet_ipaddr) + try: + DMX_ArtNet._thread = DMX_ArtNet(dmx.artnet_ipaddr) + except Exception as e: + print(e) + return DMX_ArtNet._thread.start() bpy.app.timers.register(DMX_ArtNet.run_render) @@ -203,6 +213,7 @@ def status(): return [ ("offline", "Offline", ""), ("socket_open", "Opening socket...", ""), + ("socket_error", "Error opening socket... Close other Art-Net applications, re-enable Art-Net in BlenderDMX and start the other application", ""), ("listen", "Waiting for data...", ""), ("online", "Online", ""), ("stop", "Stopping thread...", ""), diff --git a/data.py b/data.py index f44eb9d9..20e04058 100644 --- a/data.py +++ b/data.py @@ -141,6 +141,5 @@ def set_universe(universe, data, source): except Exception as e: print(e) DMX_Data._last_updated = time.time() - print("update dmx") return changed diff --git a/panels/dmx.py b/panels/dmx.py index 76845a7d..456f3f84 100644 --- a/panels/dmx.py +++ b/panels/dmx.py @@ -131,7 +131,7 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn if self.layout_type in {'DEFAULT', 'COMPACT'}: col = layout.column() col.label(text=f"{item.id}", icon=icon) - col.ui_units_x = 2 + col.ui_units_x = 3 col = layout.column() col.prop(item, "name", text="", emboss=False) col = layout.column() From 76cfce23b7c3c051091c9722b700d28d707981ba Mon Sep 17 00:00:00 2001 From: vanous Date: Thu, 28 Dec 2023 20:29:45 +0100 Subject: [PATCH 3/4] Edit XYZ in fixtures panel --- panels/fixtures.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/panels/fixtures.py b/panels/fixtures.py index cae2d1a8..06de8144 100644 --- a/panels/fixtures.py +++ b/panels/fixtures.py @@ -573,27 +573,28 @@ def draw(self, context): c.label(text="Name") c.ui_units_x = 8 - if dmx.column_fixture_id: + if dmx.column_fixture_id and not dmx.fixture_properties_editable: + c = row.column() c.label(text="F ID") c.ui_units_x = 2 - if dmx.column_unit_number: + if dmx.column_unit_number and not dmx.fixture_properties_editable: c = row.column() c.ui_units_x = 2 c.label(text="Unit #") - if dmx.column_fixture_id_numeric: + if dmx.column_fixture_id_numeric and not dmx.fixture_properties_editable: c = row.column() c.label(text="F ID #") c.ui_units_x = 2 - if dmx.column_custom_id: + if dmx.column_custom_id and not dmx.fixture_properties_editable: c = row.column() c.label(text="Cst ID") c.ui_units_x = 2 - if dmx.column_dmx_address: + if dmx.column_dmx_address and not dmx.fixture_properties_editable: c = row.column() c.ui_units_x = 2 if dmx.fixture_properties_editable: @@ -604,11 +605,6 @@ def draw(self, context): else: c.label(text="Uni.Addr") - if dmx.fixture_properties_editable: - c = row.column() - c.ui_units_x = 2 - c.label(text="Del") - layout.template_list( "DMX_UL_Fixtures", "", @@ -730,6 +726,23 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn else: c.label(text=f"{item.universe}.{item.address}") + if dmx.fixture_properties_editable: + body = None + for obj in item.collection.objects: + if obj.get("geometry_root", False): + body = obj + break + if body is not None: + col = layout.column() + col.prop(body, "location", index=0, text='') + col.ui_units_x = 3 + col = layout.column() + col.ui_units_x = 3 + col.prop(body, "location", index=1, text='') + col = layout.column() + col.prop(body, "location", index=2, text='') + col.ui_units_x = 3 + if dmx.fixture_properties_editable: col = layout.column() col.context_pointer_set("fixture", item) From 406f1a00945373af77c0eb516bc6dd4410e2a318 Mon Sep 17 00:00:00 2001 From: vanous Date: Thu, 28 Dec 2023 21:26:55 +0100 Subject: [PATCH 4/4] Addition error handling --- __init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/__init__.py b/__init__.py index 5a4c37bc..077c4409 100644 --- a/__init__.py +++ b/__init__.py @@ -1370,11 +1370,12 @@ def onDepsgraph(scene): @bpy.app.handlers.persistent def onLoadFile(scene): - if ('DMX' in bpy.data.scenes['Scene'].collection.children): - print("DMX", "File contains DMX show, linking...") - bpy.context.scene.dmx.linkFile() - else: - bpy.context.scene.dmx.unlinkFile() + if "Scene" in bpy.data.scenes: + if ('DMX' in bpy.data.scenes['Scene'].collection.children): + print("DMX", "File contains DMX show, linking...") + bpy.context.scene.dmx.linkFile() + else: + bpy.context.scene.dmx.unlinkFile() # Selection callback handle = object()