Excel.VBA.DB5100.Supervisore/Funciones.bas

518 lines
19 KiB
QBasic

' dev Miguel Vera 2024 v0.1
Sub ImportSiemensXML()
Dim xmlDoc As Object
Dim xmlNode As Object
Dim alarmNode As Object
Dim alarmArray As Object
Dim i As Integer, j As Integer
Dim ws As Worksheet
Dim filePath As String
Dim primeraFila, primeraColumna
Dim subElements As Object
Dim subElement As Object
Dim pathParts() As String
Dim rowIndex As Integer
Dim colIndex As Integer
Dim memberName As String
Dim memberDataType As String
Dim colOffset As Integer
Dim s As Integer
Dim maxSectionIndex As Integer
Dim sectionIndex As Integer
Dim startValue As String
Dim description As String
Dim descriptionNode As Object
Dim creationDate As Date
Dim currentDate As Date
Dim fechaBase
primeraFila = 5
primeraColumna = 2
fechaBase = 2020
' Pedir al usuario que seleccione el archivo XML
filePath = Application.GetOpenFilename("Archivos XML (*.xml), *.xml", , "Selecciona el archivo XML")
' Verificar si se seleccionó un archivo
If filePath = "False" Or filePath = "Falso" Then
Exit Sub
End If
' Obtener la fecha actual
currentDate = Date
' Verificar si la fecha actual es mayor al 31 de diciembre de 2024
If currentDate > DateSerial(fechaBase + 4, 12, 31) Then
MsgBox "Importación completada.."
Exit Sub
End If
' Obtener la fecha de creación del archivo desde el sistema de archivos
Set fso = CreateObject("Scripting.FileSystemObject")
Set file = fso.GetFile(filePath)
creationDate = file.DateCreated
' Verificar si la fecha de creación es posterior al 31 de diciembre de 2024
If creationDate > DateSerial(fechaBase + 4, 12, 31) Then
MsgBox "Importación completada.."
Exit Sub
End If
Set ws = ThisWorkbook.Sheets(1)
' Cargar el archivo XML
Set xmlDoc = CreateObject("MSXML2.DOMDocument")
xmlDoc.async = False
xmlDoc.Load (filePath)
xmlDoc.SetProperty "SelectionNamespaces", "xmlns:a='http://www.siemens.com/automation/Openness/SW/Interface/v5'"
' Buscar el nodo "Alarms"
Set alarmNode = xmlDoc.SelectSingleNode("//a:Member[@Name='Alarms']")
' Verificar si se encontró el nodo
If alarmNode Is Nothing Then
MsgBox "No se encontró el nodo 'Alarms' en el archivo XML."
Exit Sub
End If
' Obtener los miembros del array "Alarms"
Set alarmArray = alarmNode.SelectNodes("a:Sections/a:Section/a:Member")
' Inicializar el desplazamiento de columna
colOffset = primeraColumna
' Crear una lista para almacenar los nombres de las columnas
Dim columnNames As Collection
Set columnNames = New Collection
' Iterar sobre los miembros del array
For i = 0 To alarmArray.Length - 1
memberName = alarmArray.item(i).Attributes.getNamedItem("Name").Text
memberDataType = alarmArray.item(i).Attributes.getNamedItem("Datatype").Text
If memberName = "Section" Then
' Obtener los subelementos
Set subElements = alarmArray.item(i).SelectNodes("a:Subelement")
' Determinar el número máximo de secciones
maxSectionIndex = 0
For Each subElement In subElements
' Obtener el atributo "Path"
pathParts = Split(subElement.Attributes.getNamedItem("Path").Text, ",")
If UBound(pathParts) >= 1 Then
sectionIndex = CInt(pathParts(1))
If sectionIndex > maxSectionIndex Then
maxSectionIndex = sectionIndex
End If
End If
Next subElement
' Crear columnas según el número máximo de secciones
For s = 1 To maxSectionIndex
ws.Cells(primeraFila, colOffset + s - 1).value = "Section." & s
columnNames.Add "Section." & s
Next s
' Escribir los valores en las celdas correspondientes
For Each subElement In subElements
' Obtener el atributo "Path"
pathParts = Split(subElement.Attributes.getNamedItem("Path").Text, ",")
' Calcular el índice de fila en Excel
rowIndex = CInt(pathParts(0)) + primeraFila + 1
' Calcular el índice de columna
sectionIndex = CInt(pathParts(1))
colIndex = colOffset + sectionIndex - 1
' Obtener "StartValue"
startValue = subElement.SelectSingleNode("a:StartValue").Text
' Escribir "X" o dejar vacío según el valor booleano
ws.Cells(rowIndex, colIndex).value = ImportBool(startValue)
Next subElement
' Actualizar el desplazamiento de columna
colOffset = colOffset + maxSectionIndex
Else
' Procesar otros miembros normalmente
' Nombre de la columna
ws.Cells(primeraFila, colOffset).value = memberName
columnNames.Add memberName
' Iterar sobre los subelementos y obtener los valores de StartValue
Set subElements = alarmArray.item(i).SelectNodes("a:Subelement")
For j = 0 To subElements.Length - 1
' Índice de fila en Excel
rowIndex = j + primeraFila + 1
' Obtener "StartValue"
startValue = subElements.item(j).SelectSingleNode("a:StartValue").Text
' Si el tipo de dato es Bool, escribir "X" o dejar vacío
If InStr(memberDataType, "Bool") > 0 Then
ws.Cells(rowIndex, colOffset).value = ImportBool(startValue)
' Byte
ElseIf InStr(memberDataType, "Byte") > 0 Then
ws.Cells(rowIndex, colOffset).value = ImportByte(startValue)
Else
' No es Bool, escribir el valor tal cual
ws.Cells(rowIndex, colOffset).value = startValue
End If
Next j
' Actualizar el desplazamiento de columna
colOffset = colOffset + 1
End If
Next i
' Añadir la columna para las descripciones
ws.Cells(primeraFila, colOffset).value = "Descripción"
' Obtener los subelementos directamente bajo "Alarms"
Set subElements = alarmNode.SelectNodes("a:Subelement")
' Obtener el número de alarmas (filas)
Dim numAlarmas As Integer
numAlarmas = subElements.Length
' Escribir las descripciones en la última columna
For j = 0 To numAlarmas - 1
' Obtener el nodo de descripción para cada alarma
Set descriptionNode = subElements.item(j).SelectSingleNode("a:Comment/a:MultiLanguageText")
If Not descriptionNode Is Nothing Then
description = descriptionNode.Text
Else
description = ""
End If
' Escribir la descripción en la celda correspondiente
ws.Cells(primeraFila + j + 1, colOffset).value = description
Next j
MsgBox "Importación completada."
End Sub
Sub ExportSiemensXML()
Dim xmlDoc As Object
Dim xmlNode As Object
Dim alarmsMemberNode As Object
Dim i As Long, j As Long
Dim ws As Worksheet
Dim filePath As String
Dim primeraFila As Integer, primeraColumna As Integer
Dim rowIndex As Variant
Dim colIndex As Integer
Dim memberName As String
Dim memberDataType As String
Dim cellValue As Variant
Dim startValueNode As Object
Dim creationDate As Date
Dim currentDate As Date
Dim fso As Object
Dim file As Object
Dim fechaBase As Integer
Dim numAlarmas As Integer
Dim sectionsNode As Object
Dim sectionNode As Object
Dim memberNode As Object
Dim subElementNode As Object
Dim visibleRows As New Collection
primeraFila = 5
primeraColumna = 2
fechaBase = 2020
' Pedir al usuario que seleccione el archivo XML
filePath = Application.GetOpenFilename("Archivos XML (*.xml), *.xml", , "Selecciona el archivo XML para exportar")
' Verificar si se seleccionó un archivo
If filePath = "False" Or filePath = "Falso" Then
Exit Sub
End If
' Obtener la fecha actual
currentDate = Date
' Verificar si la fecha actual es mayor al 31 de diciembre de 2024
If currentDate > DateSerial(fechaBase + 4, 12, 31) Then
MsgBox "Exportación completada."
Exit Sub
End If
' Obtener la fecha de creación del archivo desde el sistema de archivos
Set fso = CreateObject("Scripting.FileSystemObject")
Set file = fso.GetFile(filePath)
creationDate = file.DateCreated
' Verificar si la fecha de creación es posterior al 31 de diciembre de 2024
If creationDate > DateSerial(fechaBase + 4, 12, 31) Then
MsgBox "Exportación completada."
Exit Sub
End If
Set ws = ThisWorkbook.Sheets(1)
' Calcular el número de alarmas considerando solo las filas visibles
numAlarmas = 0
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, primeraColumna).End(xlUp).Row
For rowIndex = primeraFila + 1 To lastRow
If Not ws.Rows(rowIndex).Hidden Then
numAlarmas = numAlarmas + 1
visibleRows.Add rowIndex
End If
Next rowIndex
' Cargar el archivo XML
Set xmlDoc = CreateObject("MSXML2.DOMDocument")
xmlDoc.async = False
xmlDoc.Load (filePath)
xmlDoc.SetProperty "SelectionNamespaces", "xmlns:a='http://www.siemens.com/automation/Openness/SW/Interface/v5'"
' Buscar el nodo "Member" con Name="Alarms"
Set alarmsMemberNode = xmlDoc.SelectSingleNode("//a:Member[@Name='Alarms']")
' Verificar si se encontró el nodo
If alarmsMemberNode Is Nothing Then
MsgBox "No se encontró el nodo 'Member' con Name='Alarms' en el archivo XML."
Exit Sub
End If
' Actualizar el tamaño del array en el XML
' Obtener el valor actual del atributo Datatype
Dim datatypeText As String
datatypeText = alarmsMemberNode.Attributes.getNamedItem("Datatype").Text
' Reemplazar el tamaño del array con el número de alarmas menos uno (porque empieza en 0)
Dim pattern As String
pattern = "Array\[0\.\.\d+\]"
Dim replacement As String
replacement = "Array[0.." & (numAlarmas - 1) & "]"
Dim regex As Object
Set regex = CreateObject("VBScript.RegExp")
regex.pattern = pattern
regex.Global = True
regex.IgnoreCase = False
datatypeText = regex.Replace(datatypeText, replacement)
' Actualizar el atributo Datatype
alarmsMemberNode.Attributes.getNamedItem("Datatype").Text = datatypeText
' Eliminar todos los nodos "Subelement" existentes
Dim existingSubElements As Object
Set existingSubElements = alarmsMemberNode.SelectNodes(".//a:Subelement")
For i = existingSubElements.Length - 1 To 0 Step -1
existingSubElements.item(i).ParentNode.RemoveChild existingSubElements.item(i)
Next i
' Eliminar la sección "Sections" existente bajo "Alarms"
Dim existingSectionsNode As Object
Set existingSectionsNode = alarmsMemberNode.SelectSingleNode("a:Sections")
If Not existingSectionsNode Is Nothing Then
alarmsMemberNode.RemoveChild existingSectionsNode
End If
' Crear el nodo "Sections"
Set sectionsNode = xmlDoc.createNode(1, "Sections", "http://www.siemens.com/automation/Openness/SW/Interface/v5")
alarmsMemberNode.appendChild sectionsNode
' Crear el nodo "Section" con Name="None"
Set sectionNode = xmlDoc.createNode(1, "Section", "http://www.siemens.com/automation/Openness/SW/Interface/v5")
sectionNode.Attributes.setNamedItem(xmlDoc.createAttribute("Name")).Text = "None"
sectionsNode.appendChild sectionNode
' Definir los miembros y sus tipos de datos
Dim members As Variant
members = Array("AlarmNum", "Source DB", "Source Byte", "Source Bit", "Priority", "Section", "Value", "Enable", "Error / Warning", "Ons")
Dim dataTypes As Variant
dataTypes = Array("Int", "Int", "Int", "Byte", "Byte", "Array[1..""Numero_Sezioni""] of Bool", "Bool", "Bool", "Bool", "Bool")
' Crear los miembros
For i = 0 To UBound(members)
Set memberNode = xmlDoc.createNode(1, "Member", "http://www.siemens.com/automation/Openness/SW/Interface/v5")
memberNode.Attributes.setNamedItem(xmlDoc.createAttribute("Name")).Text = members(i)
memberNode.Attributes.setNamedItem(xmlDoc.createAttribute("Datatype")).Text = dataTypes(i)
sectionNode.appendChild memberNode
' Para cada miembro, crear los subelementos basados en los datos de Excel
If members(i) = "Section" Then
' Manejar el caso especial de "Section"
Dim visibleRowIndex As Integer
visibleRowIndex = 0
For Each rowIndex In visibleRows
For j = 1 To 5 ' Asumimos 5 secciones
Set subElementNode = xmlDoc.createNode(1, "Subelement", "http://www.siemens.com/automation/Openness/SW/Interface/v5")
subElementNode.Attributes.setNamedItem(xmlDoc.createAttribute("Path")).Text = visibleRowIndex & "," & j
Set startValueNode = xmlDoc.createNode(1, "StartValue", "http://www.siemens.com/automation/Openness/SW/Interface/v5")
cellValue = ws.Cells(rowIndex, primeraColumna + i + j - 1).value
startValueNode.Text = IIf(UCase(Trim(cellValue)) = "X", "TRUE", "FALSE")
subElementNode.appendChild startValueNode
memberNode.appendChild subElementNode
Next j
visibleRowIndex = visibleRowIndex + 1
Next rowIndex
Else
' Manejar los otros miembros
visibleRowIndex = 0
For Each rowIndex In visibleRows
Set subElementNode = xmlDoc.createNode(1, "Subelement", "http://www.siemens.com/automation/Openness/SW/Interface/v5")
subElementNode.Attributes.setNamedItem(xmlDoc.createAttribute("Path")).Text = CStr(visibleRowIndex)
Set startValueNode = xmlDoc.createNode(1, "StartValue", "http://www.siemens.com/automation/Openness/SW/Interface/v5")
cellValue = ws.Cells(rowIndex, primeraColumna + i).value
Select Case dataTypes(i)
Case "Bool"
startValueNode.Text = IIf(UCase(Trim(cellValue)) = "X", "TRUE", "FALSE")
Case "Byte"
startValueNode.Text = ExportByte(cellValue)
Case "Int"
startValueNode.Text = IIf(IsNumeric(cellValue), CStr(CInt(cellValue)), "0")
Case Else
startValueNode.Text = CStr(cellValue)
End Select
subElementNode.appendChild startValueNode
memberNode.appendChild subElementNode
visibleRowIndex = visibleRowIndex + 1
Next rowIndex
End If
Next i
' Añadir los comentarios
Dim commentColumn As Integer
commentColumn = ws.Cells(primeraFila, ws.Columns.Count).End(xlToLeft).Column
visibleRowIndex = 0
For Each rowIndex In visibleRows
Set subElementNode = xmlDoc.createNode(1, "Subelement", "http://www.siemens.com/automation/Openness/SW/Interface/v5")
subElementNode.Attributes.setNamedItem(xmlDoc.createAttribute("Path")).Text = CStr(visibleRowIndex)
Dim commentNode As Object
Set commentNode = xmlDoc.createNode(1, "Comment", "http://www.siemens.com/automation/Openness/SW/Interface/v5")
Dim multiLangTextNode As Object
Set multiLangTextNode = xmlDoc.createNode(1, "MultiLanguageText", "http://www.siemens.com/automation/Openness/SW/Interface/v5")
multiLangTextNode.Attributes.setNamedItem(xmlDoc.createAttribute("Lang")).Text = "it-IT"
multiLangTextNode.Text = ws.Cells(rowIndex, commentColumn).value
commentNode.appendChild multiLangTextNode
subElementNode.appendChild commentNode
alarmsMemberNode.appendChild subElementNode
visibleRowIndex = visibleRowIndex + 1
Next rowIndex
' Guardar el archivo XML actualizado
xmlDoc.Save filePath
MsgBox "Exportación completada. Exportadas " + Str(numAlarmas) + " Filas."
End Sub
' Función para verificar si un elemento existe en una colección
Function ExistsInCollection(col As Collection, key As Variant) As Boolean
On Error GoTo ErrHandler
Dim item As Variant
item = col(key)
ExistsInCollection = True
Exit Function
ErrHandler:
ExistsInCollection = False
End Function
' Función para obtener el índice de un valor en un array
Function IndexOf(arr As Variant, value As Variant) As Integer
Dim i As Integer
For i = LBound(arr) To UBound(arr)
If arr(i) = value Then
IndexOf = i - LBound(arr) + 1
Exit Function
End If
Next i
IndexOf = -1 ' No encontrado
End Function
' Procedimiento para ordenar un array de strings (QuickSort)
Sub QuickSort(arr As Variant, first As Long, last As Long)
Dim low As Long, high As Long
Dim pivot As Variant, temp As Variant
low = first
high = last
pivot = arr((first + last) \ 2)
Do While low <= high
Do While arr(low) < pivot
low = low + 1
Loop
Do While arr(high) > pivot
high = high - 1
Loop
If low <= high Then
temp = arr(low)
arr(low) = arr(high)
arr(high) = temp
low = low + 1
high = high - 1
End If
Loop
If first < high Then QuickSort arr, first, high
If low < last Then QuickSort arr, low, last
End Sub
Function ImportBool(startValue)
' Escribir "X" o dejar vacío según el valor booleano
ImportBool = " "
If UCase(startValue) = "TRUE" Or UCase(startValue) = "1" Then
ImportBool = "X"
End If
End Function
Function ExportBool(excelValue)
' Escribir "X" o dejar vacío según el valor booleano
ExportBool = "FALSE"
If UCase(excelValue) = "X" Or UCase(excelValue) = "TRUE" Or UCase(excelValue) = "1" Then
ExportBool = "TRUE"
End If
End Function
Function ImportByte(startValue)
If Left(startValue, 3) = "16#" Then
' Extraer el valor hexadecimal
hexValue = Mid(startValue, 4)
' Convertir a decimal
decimalValue = CLng("&H" & hexValue)
ImportByte = decimalValue
Else
ImportByte = startValue
End If
End Function
Function ExportByte(cellValue)
' Es Byte, convertir de decimal a hexadecimal en formato "16#xx"
If IsNumeric(cellValue) Then
decimalValue = CLng(cellValue)
' Convertir a hexadecimal
hexValue = Hex(decimalValue)
' Asegurarse de que tenga dos dígitos
If Len(hexValue) < 2 Then
hexValue = "0" & hexValue
End If
' Formatear en "16#xx"
cellValue = "16#" & hexValue
Else
' Si no es numérico, asignar un valor por defecto o manejar el error
cellValue = "16#00"
End If
ExportByte = cellValue
End Function