You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
g85/g85/write.py

132 lines
4.1 KiB
Python

from typing import Sequence, Tuple, List, TextIO, Union
import logging
import math
from dataclasses import fields
from xml.etree import ElementTree
from .main import Map, Device
logger = logging.getLogger(__name__)
# Hack to directly pass through <![CDATA[...]]>
def _escape_cdata(text):
if text.startswith('<![CDATA[') and text.endswith(']]>'):
return text
else:
return _original_escape_cdata(text)
_original_escape_cdata = ElementTree._escape_cdata # type: ignore
ElementTree._escape_cdata = _escape_cdata # type: ignore
####
def write(maps: Sequence[Map], stream: TextIO) -> None:
el_root = ElementTree.Element('Maps')
for wmap in maps:
write_wmap(wmap, el_root)
tree = ElementTree.ElementTree(element=el_root)
ElementTree.indent(tree)
tree.write(stream)
def write_wmap(wmap: Map, el_root: ElementTree.Element) -> None:
el_map = ElementTree.SubElement(el_root, 'Map')
write_devices(wmap.devices, el_map)
map_fields = [ff.name for ff in fields(wmap)]
for field in map_fields:
if field[0].isupper():
el_map.set(field, getattr(wmap, field))
for key, value in wmap.misc.items():
if key[0].isupper() and key in map_fields:
continue
el_map.set(key, value)
def write_devices(devices: Sequence[Device], el_map: ElementTree.Element) -> None:
for device in devices:
el_device = ElementTree.SubElement(el_map, 'Device')
# ReferenceDevice
if device.reference_xy is not None:
el_ref = ElementTree.SubElement(el_device, 'ReferenceDevice')
el_ref.set('ReferenceDeviceX', str(device.reference_xy[0]))
el_ref.set('ReferenceDeviceY', str(device.reference_xy[1]))
# Row data prep
if device.map is None:
raise Exception(f'No _data for device pformat({device})')
is_decimal = device.BinType == 'Decimal'
row_texts, bin_length = prepare_data(device.map, decimal=is_decimal)
# Bins
if not device.bin_pass:
logger.warning('No bins were provided!')
bin_counts = device.bin_counts()
for bin_code, passed in device.bin_pass.items():
el_bin = ElementTree.SubElement(el_device, 'Bin')
if is_decimal:
el_bin.set('BinCode', str(bin_code).zfill(bin_length))
else:
el_bin.set('BinCode', str(bin_code))
el_bin.set('BinQuality', 'Pass' if passed else 'Fail')
el_bin.set('BinCount', str(bin_counts[bin_code]))
for row_text in row_texts:
el_row = ElementTree.SubElement(el_device, 'Row')
el_row.text = f'<![CDATA[{row_text}]]>'
# Device attribs
dev_fields = [ff.name for ff in fields(device)]
for field in dev_fields:
if field[0].isupper():
val = getattr(device, field)
if field in ('WaferSize', 'DeviceSizeX', 'DeviceSizeY', 'Orientation'):
val = f'{val:g}'
elif field in ('OriginLocation',):
val = f'{val:d}'
elif field == 'CreateDate':
val = val.strftime('%Y%m%d%H%M%S%f')[:-3]
el_device.set(field, val)
for key, value in device.misc.items():
if key[0].isupper() and key in dev_fields:
continue
el_device.set(key, value)
def prepare_data(data: List[List[Union[str, int]]], decimal: bool) -> Tuple[List[str], int]:
is_char = isinstance(data[0][0], str)
if is_char:
char_len = len(data[0][0])
else:
max_value = max(max(rr) for rr in data)
max_digits = math.ceil(math.log10(max_value))
row_texts = []
for row in data:
if is_char and char_len == 1:
row_text = ''.join(row)
elif is_char:
row_text = ' '.join(row) + ' '
else:
row_text = ' '.join(str(vv).zfill(max_digits) for vv in row) + ' '
row_texts.append(row_text)
if is_char:
return row_texts, char_len
else:
return row_texts, max_digits