2222# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2323# SOFTWARE.
2424
25+ from copy import deepcopy
2526from typing import List , Union , Optional , Tuple
2627from xml .etree import ElementTree
2728from xml .etree .ElementTree import Element
@@ -80,7 +81,9 @@ def __exit__(self, exc_type, exc_val, exc_tb):
8081class GeneralSceneDescriptionWriter :
8182 """Creates MVR zip archive with packed GeneralSceneDescription xml and other files"""
8283
83- def __init__ (self ):
84+ def __init__ (
85+ self ,
86+ ):
8487 self .version_major : str = "1"
8588 self .version_minor : str = "6"
8689 self .provider : str = "pymvr"
@@ -94,6 +97,13 @@ def __init__(self):
9497 providerVersion = self .provider_version ,
9598 )
9699
100+ def serialize_scene (self , scene : "Scene" ):
101+ scene .to_xml (parent = self .xml_root )
102+
103+ def serialize_user_data (self , user_data : "UserData" ):
104+ if user_data :
105+ user_data .to_xml (parent = self .xml_root )
106+
97107 def write_mvr (self , path : Optional [str ] = None ):
98108 if path is not None :
99109 if sys .version_info >= (3 , 9 ):
@@ -381,32 +391,32 @@ def to_xml(self, parent: Element):
381391class Addresses (BaseNode ):
382392 def __init__ (
383393 self ,
384- address : Optional [List ["Address" ]] = None ,
385- network : Optional [List ["Network" ]] = None ,
394+ addresses : Optional [List ["Address" ]] = None ,
395+ networks : Optional [List ["Network" ]] = None ,
386396 xml_node : Optional ["Element" ] = None ,
387397 * args ,
388398 ** kwargs ,
389399 ):
390- self .address = address if address is not None else []
391- self .network = network if network is not None else []
400+ self .addresses : List [ "Address" ] = addresses if addresses is not None else []
401+ self .networks : List [ "Network" ] = networks if networks is not None else []
392402 super ().__init__ (xml_node , * args , ** kwargs )
393403
394404 def _read_xml (self , xml_node : "Element" ):
395- self .address = [Address (xml_node = i ) for i in xml_node .findall ("Address" )]
396- self .network = [Network (xml_node = i ) for i in xml_node .findall ("Network" )]
405+ self .addresses = [Address (xml_node = i ) for i in xml_node .findall ("Address" )]
406+ self .networks = [Network (xml_node = i ) for i in xml_node .findall ("Network" )]
397407
398408 def to_xml (self , parent : Element ) -> Optional [Element ]:
399- if not self .address and not self .network :
409+ if not self .addresses and not self .networks :
400410 return None
401411 element = ElementTree .SubElement (parent , "Addresses" )
402- for dmx_address in self .address :
412+ for dmx_address in self .addresses :
403413 dmx_address .to_xml (element )
404- for network_address in self .network :
414+ for network_address in self .networks :
405415 network_address .to_xml (element )
406416 return element
407417
408418 def __len__ (self ):
409- return len (self .address ) + len (self .network )
419+ return len (self .addresses ) + len (self .networks )
410420
411421
412422class BaseChildNode (BaseNode ):
@@ -574,12 +584,10 @@ def populate_xml(self, element: Element):
574584
575585 if self .fixture_id is not None :
576586 ElementTree .SubElement (element , "FixtureID" ).text = str (self .fixture_id )
577-
578587 if self .fixture_id_numeric is not None :
579588 ElementTree .SubElement (element , "FixtureIDNumeric" ).text = str (
580589 self .fixture_id_numeric
581590 )
582-
583591 if self .unit_number is not None :
584592 ElementTree .SubElement (element , "UnitNumber" ).text = str (self .unit_number )
585593 if self .custom_id_type is not None :
@@ -620,8 +628,11 @@ def __str__(self):
620628
621629 def populate_xml (self , element : Element ):
622630 super ().populate_xml (element )
623- if self .geometries :
624- self .geometries .to_xml (element )
631+ if self .geometries is None :
632+ raise ValueError (
633+ f"{ type (self ).__name__ } '{ self .name } ' missing required Geometries"
634+ )
635+ self .geometries .to_xml (element )
625636
626637
627638class Data (BaseNode ):
@@ -634,6 +645,8 @@ def __init__(
634645 ):
635646 self .provider = provider
636647 self .ver = ver
648+ self .text : Optional [str ] = None
649+ self .extra_children : List [Element ] = []
637650 super ().__init__ (* args , ** kwargs )
638651
639652 def _read_xml (self , xml_node : "Element" ):
@@ -643,14 +656,20 @@ def _read_xml(self, xml_node: "Element"):
643656 ver = xml_node .attrib .get ("ver" )
644657 if ver is not None :
645658 self .ver = ver
659+ self .text = xml_node .text
660+ self .extra_children = [deepcopy (child ) for child in list (xml_node )]
646661
647662 def __str__ (self ):
648663 return f"{ self .provider } { self .ver } "
649664
650665 def to_xml (self ):
651- return ElementTree .Element (
666+ element = ElementTree .Element (
652667 type (self ).__name__ , provider = self .provider , ver = self .ver
653668 )
669+ element .text = self .text
670+ for child in self .extra_children :
671+ element .append (deepcopy (child ))
672+ return element
654673
655674
656675class AUXData (BaseNode ):
@@ -740,6 +759,8 @@ def _read_xml(self, xml_node: "Element"):
740759 self .scale_handling = ScaleHandeling (xml_node = scale_handling_node )
741760
742761 def to_xml (self ):
762+ if self .source is None :
763+ raise ValueError (f"MappingDefinition '{ self .name } ' missing required Source" )
743764 element = ElementTree .Element (
744765 type (self ).__name__ , name = self .name , uuid = self .uuid
745766 )
@@ -832,6 +853,11 @@ def to_xml(self):
832853 if self .multipatch :
833854 attributes ["multipatch" ] = self .multipatch
834855 element = ElementTree .Element (type (self ).__name__ , attributes )
856+ if self .multipatch is None :
857+ if self .fixture_id is None :
858+ self .fixture_id = "0"
859+ if self .fixture_id_numeric is None :
860+ self .fixture_id_numeric = 0
835861 self .populate_xml (element )
836862
837863 if self .focus :
@@ -1416,6 +1442,11 @@ def to_xml(self):
14161442 if self .multipatch :
14171443 attributes ["multipatch" ] = self .multipatch
14181444 element = ElementTree .Element (type (self ).__name__ , attributes )
1445+ if self .multipatch is None :
1446+ if self .fixture_id is None :
1447+ self .fixture_id = "0"
1448+ if self .fixture_id_numeric is None :
1449+ self .fixture_id_numeric = 0
14191450 self .populate_xml (element )
14201451 if self .position :
14211452 ElementTree .SubElement (element , "Position" ).text = self .position
@@ -1459,6 +1490,11 @@ def to_xml(self):
14591490 if self .multipatch :
14601491 attributes ["multipatch" ] = self .multipatch
14611492 element = ElementTree .Element (type (self ).__name__ , attributes )
1493+ if self .multipatch is None :
1494+ if self .fixture_id is None :
1495+ self .fixture_id = "0"
1496+ if self .fixture_id_numeric is None :
1497+ self .fixture_id_numeric = 0
14621498 self .populate_xml (element )
14631499
14641500 if self .position :
@@ -1499,6 +1535,11 @@ def to_xml(self):
14991535 if self .multipatch :
15001536 attributes ["multipatch" ] = self .multipatch
15011537 element = ElementTree .Element (type (self ).__name__ , attributes )
1538+ if self .multipatch is None :
1539+ if self .fixture_id is None :
1540+ self .fixture_id = "0"
1541+ if self .fixture_id_numeric is None :
1542+ self .fixture_id_numeric = 0
15021543 self .populate_xml (element )
15031544
15041545 if self .sources :
@@ -1530,10 +1571,17 @@ def to_xml(self):
15301571 if self .multipatch :
15311572 attributes ["multipatch" ] = self .multipatch
15321573 element = ElementTree .Element (type (self ).__name__ , attributes )
1574+ if self .multipatch is None :
1575+ if self .fixture_id is None :
1576+ self .fixture_id = "0"
1577+ if self .fixture_id_numeric is None :
1578+ self .fixture_id_numeric = 0
15331579 self .populate_xml (element )
15341580
15351581 if self .projections :
15361582 self .projections .to_xml (element )
1583+ else :
1584+ raise ValueError (f"Projector '{ self .name } ' missing Projections" )
15371585
15381586 return element
15391587
@@ -1749,7 +1797,7 @@ def __init__(
17491797 * args ,
17501798 ** kwargs ,
17511799 ):
1752- self .rotation = rotation
1800+ self .rotation = 0.0 if rotation is None else rotation
17531801 self .filename = filename
17541802 super ().__init__ (xml_node , * args , ** kwargs )
17551803
@@ -1814,9 +1862,10 @@ def _read_xml(self, xml_node: "Element"):
18141862 self .scale_handling = ScaleHandeling (xml_node = scale_handling_node )
18151863
18161864 def to_xml (self ):
1865+ if self .source is None :
1866+ raise ValueError ("Projection missing required Source" )
18171867 element = ElementTree .Element (type (self ).__name__ )
1818- if self .source :
1819- element .append (self .source .to_xml ())
1868+ element .append (self .source .to_xml ())
18201869 if self .scale_handling :
18211870 self .scale_handling .to_xml (element )
18221871 return element
@@ -1840,6 +1889,8 @@ def _read_xml(self, xml_node: "Element"):
18401889
18411890 def to_xml (self , parent : Element ):
18421891 element = ElementTree .SubElement (parent , type (self ).__name__ )
1892+ if len (self .projections ) == 0 :
1893+ raise ValueError ("Projections missing Projection entries" )
18431894 for projection in self .projections :
18441895 element .append (projection .to_xml ())
18451896 return element
@@ -1895,6 +1946,8 @@ def _read_xml(self, xml_node: "Element"):
18951946
18961947 def to_xml (self , parent : Element ):
18971948 element = ElementTree .SubElement (parent , type (self ).__name__ )
1949+ if len (self .sources ) == 0 :
1950+ raise ValueError ("Sources missing Source entries" )
18981951 for source in self .sources :
18991952 element .append (source .to_xml ())
19001953 return element
0 commit comments