From 0d915683b8ee100cda8a3a5d007086c43b753473 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Sat, 14 Mar 2026 13:59:49 +0100
Subject: [PATCH 01/22] move class WVP to new namespace Parsers
---
source/CLI.vb | 2 +-
source/Controllers/WaveController.vb | 18 +++++++++---------
source/{FileFormats => Parsers}/WVP.vb | 2 +-
source/Wave.vb | 2 +-
source/Wave.vbproj | 3 ++-
tests/TestWVP.vb | 26 +++++++++++++-------------
6 files changed, 27 insertions(+), 26 deletions(-)
rename source/{FileFormats => Parsers}/WVP.vb (97%)
diff --git a/source/CLI.vb b/source/CLI.vb
index ec6e3ff0..6689ecb7 100644
--- a/source/CLI.vb
+++ b/source/CLI.vb
@@ -152,7 +152,7 @@ Friend Class CLI
Throw New NotImplementedException("TEN files are currently not supported in the CLI!")
Case TimeSeriesFile.FileExtensions.WVP
- Dim wvp As New Fileformats.WVP(file_in)
+ Dim wvp As New Parsers.WVP(file_in)
Dim wvpSeries As List(Of TimeSeries) = wvp.Process()
Log.AddLogEntry(Log.levels.info, $"Imported {wvpSeries.Count} time series")
tsList.AddRange(wvpSeries)
diff --git a/source/Controllers/WaveController.vb b/source/Controllers/WaveController.vb
index f7d5c705..abbe5403 100644
--- a/source/Controllers/WaveController.vb
+++ b/source/Controllers/WaveController.vb
@@ -561,15 +561,15 @@ Friend Class WaveController
End If
tsList.Add(ts)
Next
- Call Fileformats.WVP.writeFile(tsList, dlg.FileName,
- saveRelativePaths:=dlg.SaveRelativePaths,
- saveTitle:=dlg.SaveTitle,
- saveUnit:=dlg.SaveUnit,
- saveInterpretation:=dlg.SaveInterpretation,
- saveColor:=dlg.SaveColor,
- saveLineStyle:=dlg.SaveLineStyle,
- saveLineWidth:=dlg.SaveLineWidth,
- savePointsVisibility:=dlg.SavePointsVisibility
+ Call Parsers.WVP.writeFile(tsList, dlg.FileName,
+ saveRelativePaths:=dlg.SaveRelativePaths,
+ saveTitle:=dlg.SaveTitle,
+ saveUnit:=dlg.SaveUnit,
+ saveInterpretation:=dlg.SaveInterpretation,
+ saveColor:=dlg.SaveColor,
+ saveLineStyle:=dlg.SaveLineStyle,
+ saveLineWidth:=dlg.SaveLineWidth,
+ savePointsVisibility:=dlg.SavePointsVisibility
)
MsgBox($"Wave project file {dlg.FileName} saved.", MsgBoxStyle.Information)
End If
diff --git a/source/FileFormats/WVP.vb b/source/Parsers/WVP.vb
similarity index 97%
rename from source/FileFormats/WVP.vb
rename to source/Parsers/WVP.vb
index 97b6a831..25130cb0 100644
--- a/source/FileFormats/WVP.vb
+++ b/source/Parsers/WVP.vb
@@ -17,7 +17,7 @@
'
Imports System.Text.RegularExpressions
-Namespace Fileformats
+Namespace Parsers
'''
''' Class for importing a wave project file
diff --git a/source/Wave.vb b/source/Wave.vb
index 127fe8dc..67fc9d79 100644
--- a/source/Wave.vb
+++ b/source/Wave.vb
@@ -200,7 +200,7 @@ Public Class Wave
Call Log.AddLogEntry(Log.levels.info, $"Loading Wave project file '{projectfile}'...")
- Dim wvp As New Fileformats.WVP(projectfile)
+ Dim wvp As New Parsers.WVP(projectfile)
tsList = wvp.Process()
Call Log.AddLogEntry(Log.levels.info, $"Imported {tsList.Count} timeseries")
diff --git a/source/Wave.vbproj b/source/Wave.vbproj
index 86c75f00..bfa712e2 100644
--- a/source/Wave.vbproj
+++ b/source/Wave.vbproj
@@ -348,7 +348,6 @@
-
@@ -389,6 +388,7 @@
Settings.settings
True
+
MainWindow.vb
@@ -643,6 +643,7 @@
4.6.1
+
copy $(ProjectDir)..\COPYING $(TargetDir)COPYING
diff --git a/tests/TestWVP.vb b/tests/TestWVP.vb
index f6a9a53d..b5ced450 100644
--- a/tests/TestWVP.vb
+++ b/tests/TestWVP.vb
@@ -34,7 +34,7 @@ Imports Microsoft.VisualStudio.TestTools.UnitTesting
}
For Each file As String In filesWVP
- Dim wvp As New Fileformats.WVP(file)
+ Dim wvp As New Parsers.WVP(file)
Next
End Sub
@@ -46,7 +46,7 @@ Imports Microsoft.VisualStudio.TestTools.UnitTesting
Dim fileWVP As String = IO.Path.Combine(TestData.getTestDataDir(), "WVP", "test_displayoptions.wvp")
- Dim wvp As New Fileformats.WVP(fileWVP)
+ Dim wvp As New Parsers.WVP(fileWVP)
Dim tsList As List(Of TimeSeries) = wvp.Process()
Assert.AreEqual("AA _1AB", tsList(0).Title)
@@ -71,20 +71,20 @@ Imports Microsoft.VisualStudio.TestTools.UnitTesting
'read time series using a WVP file
Dim fileIn As String = IO.Path.Combine(TestData.getTestDataDir(), "WVP", "test_displayoptions.wvp")
- Dim wvp As New Fileformats.WVP(fileIn)
+ Dim wvp As New Parsers.WVP(fileIn)
Dim tsList As List(Of TimeSeries) = wvp.Process()
'write the time series to a WVP file
Dim fileOut As String = IO.Path.Combine(TestData.getTestDataDir(), "WVP", "test_displayoptions_export.wvp")
- Call Fileformats.WVP.writeFile(tsList, fileOut,
- saveRelativePaths:=True,
- saveTitle:=True,
- saveUnit:=True,
- saveInterpretation:=True,
- saveColor:=True,
- saveLineStyle:=True,
- saveLineWidth:=True,
- savePointsVisibility:=True
+ Call Parsers.WVP.writeFile(tsList, fileOut,
+ saveRelativePaths:=True,
+ saveTitle:=True,
+ saveUnit:=True,
+ saveInterpretation:=True,
+ saveColor:=True,
+ saveLineStyle:=True,
+ saveLineWidth:=True,
+ savePointsVisibility:=True
)
'check the file contents of the written WVP file
@@ -123,7 +123,7 @@ Imports Microsoft.VisualStudio.TestTools.UnitTesting
'read time series using a WVP file
Dim file As String = IO.Path.Combine(TestData.getTestDataDir(), "WVP", "test_preserve_order.wvp")
- Dim wvp As New Fileformats.WVP(file)
+ Dim wvp As New Parsers.WVP(file)
Dim tsList As List(Of TimeSeries) = wvp.Process()
'check the order of series
From 47447d0c51480b6c8dce23522d8ffc13940ffa44 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Sat, 14 Mar 2026 15:25:09 +0100
Subject: [PATCH 02/22] introduce a Parser base class which is inherited by WVP
---
source/Parsers/Parser.vb | 214 +++++++++++++++++++++++++++++++++++++++
source/Parsers/WVP.vb | 196 ++++-------------------------------
source/Wave.vb | 4 +-
source/Wave.vbproj | 1 +
4 files changed, 233 insertions(+), 182 deletions(-)
create mode 100644 source/Parsers/Parser.vb
diff --git a/source/Parsers/Parser.vb b/source/Parsers/Parser.vb
new file mode 100644
index 00000000..dcf8bcac
--- /dev/null
+++ b/source/Parsers/Parser.vb
@@ -0,0 +1,214 @@
+'BlueM.Wave
+'Copyright (C) BlueM Dev Group
+'
+'
+'This program is free software: you can redistribute it and/or modify
+'it under the terms of the GNU Lesser General Public License as published by
+'the Free Software Foundation, either version 3 of the License, or
+'(at your option) any later version.
+'
+'This program is distributed in the hope that it will be useful,
+'but WITHOUT ANY WARRANTY; without even the implied warranty of
+'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+'GNU Lesser General Public License for more details.
+'
+'You should have received a copy of the GNU Lesser General Public License
+'along with this program. If not, see .
+'
+Namespace Parsers
+
+ '''
+ ''' Base class for parsing file import instructions
+ '''
+ Public MustInherit Class Parser
+
+ '''
+ ''' Holds reference to a file to be imported, file import settings and the series to be imported from that file with their options
+ '''
+ Protected Structure FileReference
+ Dim path As String 'path to file
+ Dim series As Dictionary(Of String, SeriesOptions) 'series name -> options
+ Dim settings As Dictionary(Of String, String) 'file import settings as key value pairs, e.g. separator, date format, etc.
+ End Structure
+
+ '''
+ ''' Holds options for importing a series, e.g. display options, custom title and unit, etc.
+ '''
+ Protected Structure SeriesOptions
+ Dim title As String
+ Dim unit As String
+ Dim color As String
+ Dim linestyle As String
+ Dim linewidth As String
+ Dim interpretation As String
+ Dim showpoints As String
+ End Structure
+
+ '''
+ ''' The path to the input file
+ '''
+ Protected InputFile As String
+
+ '''
+ ''' The input text to be parsed
+ '''
+ Protected InputText As String
+
+ Protected FileReferences As New List(Of FileReference)
+
+ '''
+ ''' Instantiates a new Parser instance with either a file or text input and calls the Parse method to parse the input
+ '''
+ ''' The path to the input file
+ ''' The input text
+ Public Sub New(Optional inputfile As String = Nothing, Optional inputtext As String = Nothing)
+ If IsNothing(inputfile) And IsNothing(inputtext) Then
+ Throw New ArgumentException("Either a file or text input must be provided!")
+ ElseIf Not IsNothing(inputfile) And Not IsNothing(inputtext) Then
+ Throw New ArgumentException("Only one of file or text input can be provided!")
+ End If
+ Me.InputFile = inputfile
+ If Not IsNothing(inputfile) Then
+ Me.InputText = IO.File.ReadAllText(inputfile, Text.Encoding.UTF8)
+ Else
+ Me.InputText = inputtext
+ End If
+ Call Me.Parse()
+ End Sub
+
+ '''
+ ''' Parses the input and stores the results in the FileReferences list
+ '''
+ Protected MustOverride Sub Parse()
+
+ '''
+ ''' Processes the FileReferences list by reading the specified series from the files and returning them as a list
+ '''
+ ''' a list of time series
+ Public Function Process() As List(Of TimeSeries)
+
+ Dim tsList As New List(Of TimeSeries)
+
+ 'loop over file list
+ For Each fileRef As FileReference In FileReferences
+
+ Log.AddLogEntry(Log.levels.info, $"Reading file {fileRef.path} ...")
+
+ 'get an instance of the file
+ Dim fileInstance As TimeSeriesFile = TimeSeriesFile.getInstance(fileRef.path)
+
+ 'apply custom file import settings
+ If fileRef.settings.Count > 0 Then
+ For Each key As String In fileRef.settings.Keys
+ Dim value As String = fileRef.settings(key)
+ Try
+ Select Case key.ToLower()
+ Case "iscolumnseparated"
+ fileInstance.IsColumnSeparated = If(value.ToLower() = "true", True, False)
+ Case "separator"
+ fileInstance.Separator = New Character(value)
+ Case "dateformat"
+ fileInstance.Dateformat = value
+ Case "decimalseparator"
+ fileInstance.DecimalSeparator = New Character(value)
+ Case "ilineheadings"
+ fileInstance.iLineHeadings = Convert.ToInt32(value)
+ Case "ilineunits"
+ fileInstance.iLineUnits = Convert.ToInt32(value)
+ Case "ilinedata"
+ fileInstance.iLineData = Convert.ToInt32(value)
+ Case "useunits"
+ fileInstance.UseUnits = If(value.ToLower() = "true", True, False)
+ Case "columnwidth"
+ fileInstance.ColumnWidth = Convert.ToInt32(value)
+ Case "datetimecolumnindex"
+ fileInstance.DateTimeColumnIndex = Convert.ToInt32(value)
+ Case Else
+ Log.AddLogEntry(Log.levels.warning, $"Setting '{key}' was not recognized and was ignored!")
+ End Select
+ Catch ex As Exception
+ Log.AddLogEntry(Log.levels.warning, $"Setting '{key}' with value '{value}' could not be parsed and was ignored!")
+ End Try
+ Next
+ 'reread columns with new settings
+ fileInstance.readSeriesInfo()
+ End If
+
+ 'select series for importing
+ If fileRef.series.Count = 0 Then
+ 'read all series contained in the file
+ Call fileInstance.selectAllSeries()
+ Else
+ 'loop over series names
+ Dim seriesNames As List(Of String) = fileRef.series.Keys.ToList()
+ Dim seriesNotFound As New List(Of String)
+ For Each seriesName As String In seriesNames
+ Dim found As Boolean = fileInstance.selectSeries(seriesName)
+ If Not found Then
+ seriesNotFound.Add(seriesName)
+ Log.AddLogEntry(Log.levels.error, $"Series {seriesName} not found in file, skipping series!")
+ End If
+ Next
+ 'remove series that were not found from the list
+ For Each seriesName As String In seriesNotFound
+ seriesNames.Remove(seriesName)
+ Next
+ 'if no series remain to be imported, abort reading the file altogether
+ If seriesNames.Count = 0 Then
+ Log.AddLogEntry(Log.levels.error, "No series left to import, skipping file!")
+ Continue For
+ End If
+
+ End If
+
+ 'read the file
+ fileInstance.readFile()
+
+ 'Log
+ Call Log.AddLogEntry(Log.levels.info, $"File '{fileRef.path}' imported successfully!")
+
+ 'store the series
+ For Each ts As TimeSeries In fileInstance.TimeSeries.Values
+ 'set series options
+ If fileRef.series.ContainsKey(ts.Title) Then
+ Dim options As SeriesOptions = fileRef.series(ts.Title)
+ 'options affecting the time series itself
+ If Not IsNothing(options.title) Then
+ ts.Title = options.title
+ End If
+ If Not IsNothing(options.unit) Then
+ ts.Unit = options.unit
+ End If
+ If Not IsNothing(options.interpretation) Then
+ If Not [Enum].IsDefined(GetType(TimeSeries.InterpretationEnum), options.interpretation) Then
+ Log.AddLogEntry(levels.warning, $"Interpretation {options.interpretation} is not recognized!")
+ Else
+ ts.Interpretation = [Enum].Parse(GetType(TimeSeries.InterpretationEnum), options.interpretation)
+ End If
+ End If
+ 'display options
+ If Not IsNothing(options.color) Then
+ ts.DisplayOptions.SetColor(options.color)
+ End If
+ If Not IsNothing(options.linestyle) Then
+ ts.DisplayOptions.SetLineStyle(options.linestyle)
+ End If
+ If Not IsNothing(options.linewidth) Then
+ ts.DisplayOptions.SetLineWidth(options.linewidth)
+ End If
+ If Not IsNothing(options.showpoints) Then
+ ts.DisplayOptions.SetShowPoints(options.showpoints)
+ End If
+ End If
+ 'store the time series in the list
+ tsList.Add(ts)
+ Next
+ Next
+
+ Return tsList
+
+ End Function
+
+ End Class
+
+End Namespace
\ No newline at end of file
diff --git a/source/Parsers/WVP.vb b/source/Parsers/WVP.vb
index 25130cb0..bcf475d2 100644
--- a/source/Parsers/WVP.vb
+++ b/source/Parsers/WVP.vb
@@ -20,51 +20,26 @@ Imports System.Text.RegularExpressions
Namespace Parsers
'''
- ''' Class for importing a wave project file
+ ''' Class for parsing a wave project file
''' https://wiki.bluemodel.org/index.php/Wave_project_file
'''
Public Class WVP
-
- Private projectfile As String
-
- Private Structure FileEntry
- Dim path As String 'path to file
- Dim series As Dictionary(Of String, SeriesOptions) 'series name -> options
- Dim settings As Dictionary(Of String, String) 'file import settings as key value pairs, e.g. separator, date format, etc.
- End Structure
-
- Private Structure SeriesOptions
- Dim title As String
- Dim unit As String
- Dim color As String
- Dim linestyle As String
- Dim linewidth As String
- Dim interpretation As String
- Dim showpoints As String
- End Structure
-
- Private fileEntries As New List(Of FileEntry)
+ Inherits Parser
Public Sub New(file As String)
-
- Me.projectfile = file
-
- Call Me.Parse()
-
+ MyBase.New(inputfile:=file)
End Sub
'''
''' Parses the project file
'''
- Private Sub Parse()
+ Protected Overrides Sub Parse()
- Dim fstr As IO.FileStream
- Dim strRead As IO.StreamReader
Dim line, parts(), file, seriesName As String
- Log.AddLogEntry(Log.levels.info, $"Parsing Wave project file {projectfile} ...")
+ Log.AddLogEntry(Log.levels.info, $"Parsing Wave project file {MyBase.InputFile} ...")
- 'read project file
+ 'parse Wave project file
'file format (all whitespace is optional):
'
@@ -76,20 +51,15 @@ Namespace Parsers
' series=series4
' series=series5
'
- fstr = New IO.FileStream(projectfile, IO.FileMode.Open)
- strRead = New IO.StreamReader(fstr, Text.Encoding.UTF8)
-
file = ""
- line = strRead.ReadLine()
- While Not IsNothing(line)
+ For Each line In Me.inputText.Split({vbCrLf, vbLf}, StringSplitOptions.RemoveEmptyEntries)
line = line.Trim() 'get rid of whitespace
If line.StartsWith("#") Then
'skip comments
- line = strRead.ReadLine()
- Continue While
+ Continue For
End If
If line.ToLower().StartsWith("file=") Then
@@ -97,10 +67,10 @@ Namespace Parsers
file = line.Split("=".ToCharArray(), 2)(1).Trim()
If Not IO.Path.IsPathRooted(file) Then
'it's a relative path: construct the full path relative to the project file
- file = IO.Path.GetFullPath(IO.Path.Combine(IO.Path.GetDirectoryName(projectfile), file))
+ file = IO.Path.GetFullPath(IO.Path.Combine(IO.Path.GetDirectoryName(MyBase.InputFile), file))
End If
'store file entry
- fileEntries.Add(New FileEntry With {
+ FileReferences.Add(New FileReference With {
.path = file,
.series = New Dictionary(Of String, SeriesOptions)(),
.settings = New Dictionary(Of String, String)()
@@ -173,9 +143,9 @@ Namespace Parsers
End If
End If
'add series to last entry of filelist
- If fileEntries.Count > 0 Then
- If Not fileEntries.Last().series.ContainsKey(seriesName) Then
- fileEntries.Last().series.Add(seriesName, options)
+ If FileReferences.Count > 0 Then
+ If Not FileReferences.Last().series.ContainsKey(seriesName) Then
+ FileReferences.Last().series.Add(seriesName, options)
Else
'duplicate series
Log.AddLogEntry(Log.levels.warning, $"Series {seriesName} is specified more than once, only the first mention will be processed!")
@@ -195,9 +165,9 @@ Namespace Parsers
key = parts(0).Trim()
value = parts(1).Trim()
'add settings to last entry of file list
- If fileEntries.Count > 0 Then
- If Not fileEntries.Last().settings.ContainsKey(key) Then
- fileEntries.Last().settings.Add(key, value)
+ If FileReferences.Count > 0 Then
+ If Not FileReferences.Last().settings.ContainsKey(key) Then
+ FileReferences.Last().settings.Add(key, value)
Else
'duplicate setting
Log.AddLogEntry(Log.levels.warning, $"File import setting {key} is specified more than once, only the first mention will be processed!")
@@ -209,141 +179,9 @@ Namespace Parsers
Else
'ignore any other lines
End If
- line = strRead.ReadLine()
- End While
-
- strRead.Close()
- fstr.Close()
-
- End Sub
-
- '''
- ''' Reads all timeseries from all files as specified in the project file
- '''
- ''' the list of time series
- Public Function Process() As List(Of TimeSeries)
-
- Dim tsList As New List(Of TimeSeries)
-
- 'loop over file list
- For Each fileEntry As FileEntry In fileEntries
-
- Log.AddLogEntry(Log.levels.info, $"Reading file {fileEntry.path} ...")
-
- 'get an instance of the file
- Dim fileInstance As TimeSeriesFile = TimeSeriesFile.getInstance(fileEntry.path)
-
- 'apply custom file import settings
- If fileEntry.settings.Count > 0 Then
- For Each key As String In fileEntry.settings.Keys
- Dim value As String = fileEntry.settings(key)
- Try
- Select Case key.ToLower()
- Case "iscolumnseparated"
- fileInstance.IsColumnSeparated = If(value.ToLower() = "true", True, False)
- Case "separator"
- fileInstance.Separator = New Character(value)
- Case "dateformat"
- fileInstance.Dateformat = value
- Case "decimalseparator"
- fileInstance.DecimalSeparator = New Character(value)
- Case "ilineheadings"
- fileInstance.iLineHeadings = Convert.ToInt32(value)
- Case "ilineunits"
- fileInstance.iLineUnits = Convert.ToInt32(value)
- Case "ilinedata"
- fileInstance.iLineData = Convert.ToInt32(value)
- Case "useunits"
- fileInstance.UseUnits = If(value.ToLower() = "true", True, False)
- Case "columnwidth"
- fileInstance.ColumnWidth = Convert.ToInt32(value)
- Case "datetimecolumnindex"
- fileInstance.DateTimeColumnIndex = Convert.ToInt32(value)
- Case Else
- Log.AddLogEntry(Log.levels.warning, $"Setting '{key}' was not recognized and was ignored!")
- End Select
- Catch ex As Exception
- Log.AddLogEntry(Log.levels.warning, $"Setting '{key}' with value '{value}' could not be parsed and was ignored!")
- End Try
- Next
- 'reread columns with new settings
- fileInstance.readSeriesInfo()
- End If
-
- 'select series for importing
- If fileEntry.series.Count = 0 Then
- 'read all series contained in the file
- Call fileInstance.selectAllSeries()
- Else
- 'loop over series names
- Dim seriesNames As List(Of String) = fileEntry.series.Keys.ToList()
- Dim seriesNotFound As New List(Of String)
- For Each seriesName As String In seriesNames
- Dim found As Boolean = fileInstance.selectSeries(seriesName)
- If Not found Then
- seriesNotFound.Add(seriesName)
- Log.AddLogEntry(Log.levels.error, $"Series {seriesName} not found in file, skipping series!")
- End If
- Next
- 'remove series that were not found from the list
- For Each seriesName As String In seriesNotFound
- seriesNames.Remove(seriesName)
- Next
- 'if no series remain to be imported, abort reading the file altogether
- If seriesNames.Count = 0 Then
- Log.AddLogEntry(Log.levels.error, "No series left to import, skipping file!")
- Continue For
- End If
-
- End If
-
- 'read the file
- fileInstance.readFile()
-
- 'Log
- Call Log.AddLogEntry(Log.levels.info, $"File '{fileEntry.path}' imported successfully!")
-
- 'store the series
- For Each ts As TimeSeries In fileInstance.TimeSeries.Values
- 'set series options
- If fileEntry.series.ContainsKey(ts.Title) Then
- Dim options As SeriesOptions = fileEntry.series(ts.Title)
- 'options affecting the time series itself
- If Not IsNothing(options.title) Then
- ts.Title = options.title
- End If
- If Not IsNothing(options.unit) Then
- ts.Unit = options.unit
- End If
- If Not IsNothing(options.interpretation) Then
- If Not [Enum].IsDefined(GetType(TimeSeries.InterpretationEnum), options.interpretation) Then
- Log.AddLogEntry(levels.warning, $"Interpretation {options.interpretation} is not recognized!")
- Else
- ts.Interpretation = [Enum].Parse(GetType(TimeSeries.InterpretationEnum), options.interpretation)
- End If
- End If
- 'display options
- If Not IsNothing(options.color) Then
- ts.DisplayOptions.SetColor(options.color)
- End If
- If Not IsNothing(options.linestyle) Then
- ts.DisplayOptions.SetLineStyle(options.linestyle)
- End If
- If Not IsNothing(options.linewidth) Then
- ts.DisplayOptions.SetLineWidth(options.linewidth)
- End If
- If Not IsNothing(options.showpoints) Then
- ts.DisplayOptions.SetShowPoints(options.showpoints)
- End If
- End If
- 'store the time series in the list
- tsList.Add(ts)
- Next
Next
- Return tsList
-
- End Function
+ End Sub
'''
''' Write a Wave project file
diff --git a/source/Wave.vb b/source/Wave.vb
index 67fc9d79..ee01cfff 100644
--- a/source/Wave.vb
+++ b/source/Wave.vb
@@ -196,12 +196,10 @@ Public Class Wave
Private Sub Load_WVP(projectfile As String)
Try
- Dim tsList As List(Of TimeSeries)
-
Call Log.AddLogEntry(Log.levels.info, $"Loading Wave project file '{projectfile}'...")
Dim wvp As New Parsers.WVP(projectfile)
- tsList = wvp.Process()
+ Dim tsList As List(Of TimeSeries) = wvp.Process()
Call Log.AddLogEntry(Log.levels.info, $"Imported {tsList.Count} timeseries")
diff --git a/source/Wave.vbproj b/source/Wave.vbproj
index bfa712e2..f3280213 100644
--- a/source/Wave.vbproj
+++ b/source/Wave.vbproj
@@ -388,6 +388,7 @@
Settings.settings
True
+
From 254033a319a6939c4970d0ea85b6097ff033b043 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Sat, 14 Mar 2026 16:47:52 +0100
Subject: [PATCH 03/22] Make FileReference a proper class and add docstrings
---
source/Parsers/Parser.vb | 32 ++++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)
diff --git a/source/Parsers/Parser.vb b/source/Parsers/Parser.vb
index dcf8bcac..b97b4d25 100644
--- a/source/Parsers/Parser.vb
+++ b/source/Parsers/Parser.vb
@@ -23,13 +23,33 @@ Namespace Parsers
Public MustInherit Class Parser
'''
- ''' Holds reference to a file to be imported, file import settings and the series to be imported from that file with their options
+ ''' A reference to a file to be imported with all information needed for importing series from that file
'''
- Protected Structure FileReference
- Dim path As String 'path to file
- Dim series As Dictionary(Of String, SeriesOptions) 'series name -> options
- Dim settings As Dictionary(Of String, String) 'file import settings as key value pairs, e.g. separator, date format, etc.
- End Structure
+ Protected Class FileReference
+
+ '''
+ ''' The path to the file to be imported
+ '''
+ Public path As String
+
+ '''
+ ''' The series to be imported from the file with their options, where the key is the name of the series as it appears in the file and the value is a SeriesOptions object containing options for importing that series
+ ''' An empty dictionary means that all series contained in the file should be imported with default options
+ '''
+ Public series As Dictionary(Of String, SeriesOptions)
+
+ '''
+ ''' File import settings as key value pairs, e.g. separator, date format, etc.
+ ''' An empty dictionary means that default settings should be used for importing the file.
+ '''
+ Public settings As Dictionary(Of String, String)
+
+ Public Sub New()
+ Me.series = New Dictionary(Of String, SeriesOptions)
+ Me.settings = New Dictionary(Of String, String)
+ End Sub
+
+ End Class
'''
''' Holds options for importing a series, e.g. display options, custom title and unit, etc.
From b70a36c8dcda2d7d3e397ad412a01ad4ed42faf7 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Sat, 14 Mar 2026 16:48:40 +0100
Subject: [PATCH 04/22] Add TalsimClipboard Parser class
---
source/Parsers/TalsimClipboard.vb | 211 ++++++++++++++++++++++++++++++
source/Wave.vb | 173 +++---------------------
source/Wave.vbproj | 1 +
3 files changed, 227 insertions(+), 158 deletions(-)
create mode 100644 source/Parsers/TalsimClipboard.vb
diff --git a/source/Parsers/TalsimClipboard.vb b/source/Parsers/TalsimClipboard.vb
new file mode 100644
index 00000000..0ca019a0
--- /dev/null
+++ b/source/Parsers/TalsimClipboard.vb
@@ -0,0 +1,211 @@
+'BlueM.Wave
+'Copyright (C) BlueM Dev Group
+'
+'
+'This program is free software: you can redistribute it and/or modify
+'it under the terms of the GNU Lesser General Public License as published by
+'the Free Software Foundation, either version 3 of the License, or
+'(at your option) any later version.
+'
+'This program is distributed in the hope that it will be useful,
+'but WITHOUT ANY WARRANTY; without even the implied warranty of
+'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+'GNU Lesser General Public License for more details.
+'
+'You should have received a copy of the GNU Lesser General Public License
+'along with this program. If not, see .
+'
+Imports System.Text.RegularExpressions
+
+Namespace Parsers
+
+ '''
+ ''' Class for parsing Talsim clipboard content
+ '''
+ Public Class TalsimClipboard
+ Inherits Parser
+
+ Public Sub New(clipboardtext As String)
+ MyBase.New(inputtext:=clipboardtext)
+ End Sub
+
+ '''
+ ''' Parses the clipboard content
+ '''
+ Protected Overrides Sub Parse()
+
+ 'Examples:
+
+ '[SETTINGS]
+ 'Count = 1
+ '[Zeitreihe1]
+ 'SydroTyp=SydroErgZre
+ 'ZRFormat=4
+ 'ID=362
+ 'Extension=.WEL
+ 'Kennung=S000
+ 'KennungLang={S000} {1AB, HYO} Ablauf_1
+ 'Zustand=1AB
+ 'Datei=D:\Talsim-NG\customers\WVER\projectData\felix\dataBase\Felix_data\00000362.WEL
+ 'GeaendertAm=
+ 'Modell=TALSIM
+ 'Herkunft=simuliert
+ 'Interpretation=2
+ 'SimVariante=Test Langzeit/HWMerkmal_HWGK_v02
+ 'Simulation=Test Langzeit
+ 'Einheit=m3/s
+ 'EndZeitreihe
+
+ '[SETTINGS]
+ 'Count=2
+ '[Zeitreihe1]
+ 'SydroTyp=SydroBinZre
+ 'ZRFormat=99
+ 'ID=1041
+ 'Extension=.BIN
+ 'Kennung=Sce10, E038, C38 (TA_Tekeze TK 04B)
+ 'KennungLang=Sce10, E038, C38 (TA_Tekeze TK 04B), m3/s
+ 'Datei=C:\Talsim-NG\customers\Nile\projectData\hubert\dataBase\hubert_zre\00001041.BIN
+ 'Einheit=m3/s
+ 'Modell=TALSIM
+ 'Interpretation=1
+ 'EndZeitreihe
+ '[Zeitreihe2]
+ 'SydroTyp=SydroBinZre
+ 'ZRFormat=99
+ 'ID=1042
+ 'Extension=.BIN
+ 'Kennung=Sce10, E039, C39 (TA_TK5)
+ 'KennungLang=Sce10, E039, C39 (TA_TK5), m3/s
+ 'Datei=C:\Talsim-NG\customers\Nile\projectData\hubert\dataBase\hubert_zre\00001042.BIN
+ 'Einheit=m3/s
+ 'Modell=TALSIM
+ 'Interpretation=1
+ 'EndZeitreihe
+
+ 'parse clipboard contents
+ Dim m As Match
+ Dim i_series As Integer
+ Dim parts() As String
+ Dim zreblock As Boolean
+ Dim data As New List(Of Dictionary(Of String, String)) '[{zreparams1},{zreparams2},...]
+ Dim file, name As String
+
+ zreblock = False
+ For Each line As String In InputText.Split(eol)
+
+ line = line.Trim()
+
+ m = Regex.Match(line, "\[Zeitreihe(\d+)\]")
+ If m.Success Then
+ i_series = m.Groups(1).Value
+ data.Add(New Dictionary(Of String, String))
+ zreblock = True
+ End If
+
+ If zreblock Then
+ If line.Contains("=") Then
+ parts = line.Split("=")
+ data(i_series - 1).Add(parts(0), parts(1))
+ ElseIf line = "EndZeitreihe" Then
+ zreblock = False
+ Continue For
+ End If
+ End If
+
+ Next
+
+ If data.Count = 0 Then
+ Throw New Exception("No series could be parsed from TALSIM clipboard content!")
+ End If
+
+ 'initiate parsing of series
+ For Each params As Dictionary(Of String, String) In data
+
+ ' check all required parameters are present
+ Dim expectedKeys As New List(Of String) From {
+ "Datei",
+ "ZRFormat",
+ "Kennung",
+ "Interpretation"
+ }
+ For Each key As String In expectedKeys
+ If Not params.ContainsKey(key) Then
+ Throw New Exception($"Missing required entry '{key}' in clipboard content!")
+ End If
+ Next
+
+ file = params("Datei")
+
+ Select Case params("ZRFormat")
+ Case "4" 'WEL file
+
+ If Not params.ContainsKey("Zustand") Then
+ Throw New Exception("Missing required entry 'Zustand' in clipboard content!")
+ End If
+
+ 'build series name
+ If params("Kennung") = "ZPG" Then
+ 'handle control groups
+ name = "KGRP_" & params("Zustand")
+ Else
+ name = params("Kennung").PadRight(4, " ") & "_" & params("Zustand")
+ End If
+
+ If Not IO.File.Exists(file) Then
+ 'A WEL/WBL file may be zipped within a WLZIP file, so try extracting it from there
+ If Fileformats.WEL.extractFromWLZIP(file) Then
+ 'TODO #136: we should ideally clean up the extracted file later
+ End If
+ End If
+
+ 'convert interpretation value to string
+ Dim interpretationValue As Integer = Integer.Parse(params("Interpretation"))
+ Dim interpretationString As String = [Enum].GetName(GetType(TimeSeries.InterpretationEnum), interpretationValue)
+
+ Dim fileRef As New FileReference() With {
+ .path = file,
+ .series = New Dictionary(Of String, SeriesOptions)() From {
+ {name, New SeriesOptions() With {.interpretation = interpretationString}}
+ }
+ }
+ FileReferences.Add(fileRef)
+
+ Case "99" 'BIN file
+
+ If Not params.ContainsKey("Einheit") Then
+ Throw New Exception("Missing required entry 'Einheit' in clipboard content!")
+ End If
+
+ name = params("Kennung")
+
+ 'convert interpretation value to string
+ Dim interpretationValue As Integer = Integer.Parse(params("Interpretation"))
+ Dim interpretationString As String = [Enum].GetName(GetType(TimeSeries.InterpretationEnum), interpretationValue)
+
+ Dim fileRef As New FileReference() With {
+ .path = file,
+ .series = New Dictionary(Of String, SeriesOptions)() From {
+ {
+ IO.Path.GetFileName(file), New SeriesOptions() With {
+ .title = name,
+ .unit = params("Einheit"),
+ .interpretation = interpretationString
+ }
+ }
+ }
+ }
+ FileReferences.Add(fileRef)
+
+ Case Else
+ Throw New Exception($"Unsupported value {params("ZRFormat")} for ZRFormat!")
+
+ End Select
+
+ Next
+
+ End Sub
+
+ End Class
+
+End Namespace
\ No newline at end of file
diff --git a/source/Wave.vb b/source/Wave.vb
index ee01cfff..a7110afc 100644
--- a/source/Wave.vb
+++ b/source/Wave.vb
@@ -283,170 +283,27 @@ Public Class Wave
'''
Private Sub LoadFromClipboard_TALSIM(clipboardtext As String)
- 'Examples:
-
- '[SETTINGS]
- 'Count = 1
- '[Zeitreihe1]
- 'SydroTyp=SydroErgZre
- 'ZRFormat=4
- 'ID=362
- 'Extension=.WEL
- 'Kennung=S000
- 'KennungLang={S000} {1AB, HYO} Ablauf_1
- 'Zustand=1AB
- 'Datei=D:\Talsim-NG\customers\WVER\projectData\felix\dataBase\Felix_data\00000362.WEL
- 'GeaendertAm=
- 'Modell=TALSIM
- 'Herkunft=simuliert
- 'Interpretation=2
- 'SimVariante=Test Langzeit/HWMerkmal_HWGK_v02
- 'Simulation=Test Langzeit
- 'Einheit=m3/s
- 'EndZeitreihe
-
- '[SETTINGS]
- 'Count=2
- '[Zeitreihe1]
- 'SydroTyp=SydroBinZre
- 'ZRFormat=99
- 'ID=1041
- 'Extension=.BIN
- 'Kennung=Sce10, E038, C38 (TA_Tekeze TK 04B)
- 'KennungLang=Sce10, E038, C38 (TA_Tekeze TK 04B), m3/s
- 'Datei=C:\Talsim-NG\customers\Nile\projectData\hubert\dataBase\hubert_zre\00001041.BIN
- 'Einheit=m3/s
- 'Modell=TALSIM
- 'Interpretation=1
- 'EndZeitreihe
- '[Zeitreihe2]
- 'SydroTyp=SydroBinZre
- 'ZRFormat=99
- 'ID=1042
- 'Extension=.BIN
- 'Kennung=Sce10, E039, C39 (TA_TK5)
- 'KennungLang=Sce10, E039, C39 (TA_TK5), m3/s
- 'Datei=C:\Talsim-NG\customers\Nile\projectData\hubert\dataBase\hubert_zre\00001042.BIN
- 'Einheit=m3/s
- 'Modell=TALSIM
- 'Interpretation=1
- 'EndZeitreihe
-
- 'parse clipboard contents
- Dim m As Match
- Dim i_series As Integer
- Dim parts() As String
- Dim zreblock As Boolean
- Dim data As New List(Of Dictionary(Of String, String)) '[{zreparams1},{zreparams2},...]
- Dim file, name As String
- Dim fileInstance As TimeSeriesFile
- Dim ts As TimeSeries
-
- zreblock = False
- For Each line As String In clipboardtext.Split(eol)
- line = line.Trim()
-
- m = Regex.Match(line, "\[Zeitreihe(\d+)\]")
- If m.Success Then
- i_series = m.Groups(1).Value
- data.Add(New Dictionary(Of String, String))
- zreblock = True
- End If
-
- If zreblock Then
- If line.Contains("=") Then
- parts = line.Split("=")
- data(i_series - 1).Add(parts(0), parts(1))
- ElseIf line = "EndZeitreihe" Then
- zreblock = False
- Continue For
- End If
- End If
+ Try
+ Call Log.AddLogEntry(Log.levels.info, $"Parsing Talsim clipboard content...")
- Next
+ Dim wvp As New Parsers.TalsimClipboard(clipboardtext)
+ Dim tsList As List(Of TimeSeries) = wvp.Process()
- If data.Count = 0 Then
- Throw New Exception("No series could be parsed from TALSIM clipboard content!")
- End If
+ Call Log.AddLogEntry(Log.levels.info, $"Imported {tsList.Count} timeseries")
- 'initiate loading of series
- For Each params As Dictionary(Of String, String) In data
-
- ' check all required parameters are present
- Dim expectedKeys As New List(Of String) From {
- "Datei",
- "ZRFormat",
- "Kennung",
- "Interpretation"
- }
- For Each key As String In expectedKeys
- If Not params.ContainsKey(key) Then
- Throw New Exception($"Missing required entry '{key}' in clipboard content!")
- End If
+ 'import the series
+ Call Log.AddLogEntry(Log.levels.info, "Loading series in chart...")
+ For Each ts As TimeSeries In tsList
+ Call Me.Import_Series(ts)
Next
- file = params("Datei")
-
- Select Case params("ZRFormat")
- Case "4" 'WEL file
-
- If Not params.ContainsKey("Zustand") Then
- Throw New Exception("Missing required entry 'Zustand' in clipboard content!")
- End If
-
- 'build series name
- If params("Kennung") = "ZPG" Then
- 'handle control groups
- name = "KGRP_" & params("Zustand")
- Else
- name = params("Kennung").PadRight(4, " ") & "_" & params("Zustand")
- End If
-
- 'read file
- Log.AddLogEntry(Log.levels.info, $"Loading file {file} ...")
- fileInstance = TimeSeriesFile.getInstance(file)
-
- 'read series from file
- ts = fileInstance.getTimeSeries(name)
-
- 'set interpretation
- ts.Interpretation = params("Interpretation")
-
- 'import series
- Call Me.Import_Series(ts)
-
- Case "99" 'BIN file
-
- If Not params.ContainsKey("Einheit") Then
- Throw New Exception("Missing required entry 'Einheit' in clipboard content!")
- End If
-
- name = params("Kennung")
-
- 'read file
- Log.AddLogEntry(Log.levels.info, $"Loading file {file} ...")
- fileInstance = TimeSeriesFile.getInstance(file)
-
- 'read series from file
- fileInstance.readFile()
- ts = fileInstance.TimeSeries.First.Value
-
- 'add metadata
- ts.Title = name
- ts.Unit = params("Einheit")
-
- 'set interpretation
- ts.Interpretation = params("Interpretation")
-
- 'import series
- Call Me.Import_Series(ts)
-
- Case Else
- Throw New Exception($"Unsupported value {params("ZRFormat")} for ZRFormat!")
-
- End Select
+ 'Log
+ Call Log.AddLogEntry(Log.levels.info, $"Talsim clipboard content parsed successfully!")
- Next
+ Catch ex As Exception
+ MsgBox("Error while processing Talsim clipboard content:" & eol & ex.Message, MsgBoxStyle.Critical)
+ Call Log.AddLogEntry(Log.levels.error, "Error while processing Talsim clipboard content:" & eol & ex.Message)
+ End Try
End Sub
diff --git a/source/Wave.vbproj b/source/Wave.vbproj
index f3280213..15813c08 100644
--- a/source/Wave.vbproj
+++ b/source/Wave.vbproj
@@ -389,6 +389,7 @@
True
+
From 739862a40b014f602e8340e0fde34f3cfaf90430 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Sat, 14 Mar 2026 17:01:27 +0100
Subject: [PATCH 05/22] add a Parser.verifyFormat method
---
source/Parsers/Parser.vb | 5 +++++
source/Parsers/TalsimClipboard.vb | 10 ++++++++++
source/Wave.vb | 31 +++++++++++++------------------
3 files changed, 28 insertions(+), 18 deletions(-)
diff --git a/source/Parsers/Parser.vb b/source/Parsers/Parser.vb
index b97b4d25..b171db8b 100644
--- a/source/Parsers/Parser.vb
+++ b/source/Parsers/Parser.vb
@@ -96,6 +96,11 @@ Namespace Parsers
Call Me.Parse()
End Sub
+ Public Shared Function verifyFormat() As Boolean
+ 'default implementation always returns true, can be overridden in subclasses to implement specific format verification
+ Return True
+ End Function
+
'''
''' Parses the input and stores the results in the FileReferences list
'''
diff --git a/source/Parsers/TalsimClipboard.vb b/source/Parsers/TalsimClipboard.vb
index 0ca019a0..a17bb950 100644
--- a/source/Parsers/TalsimClipboard.vb
+++ b/source/Parsers/TalsimClipboard.vb
@@ -29,6 +29,16 @@ Namespace Parsers
MyBase.New(inputtext:=clipboardtext)
End Sub
+ Public Overloads Shared Function verifyFormat(clipboardtext As String) As Boolean
+ 'check if content contains expected header
+ If clipboardtext.Contains("SydroTyp=SydroErgZre") Or
+ clipboardtext.Contains("SydroTyp=SydroBinZre") Then
+ Return True
+ Else
+ Return False
+ End If
+ End Function
+
'''
''' Parses the clipboard content
'''
diff --git a/source/Wave.vb b/source/Wave.vb
index a7110afc..df376953 100644
--- a/source/Wave.vb
+++ b/source/Wave.vb
@@ -238,30 +238,25 @@ Public Class Wave
Dim clipboardtext As String
clipboardtext = Clipboard.GetText(TextDataFormat.Text)
- If clipboardtext.Contains("SydroTyp=SydroErgZre") Or
- clipboardtext.Contains("SydroTyp=SydroBinZre") Then
- 'it's a clipboard entry from TALSIM!
-
+ If Parsers.TalsimClipboard.verifyFormat(clipboardtext) Then
'ask the user for confirmation
dlgres = MessageBox.Show($"TALSIM clipboard content detected!{eol}Load series in Wave?", "Load from clipboard", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
- If Not dlgres = Windows.Forms.DialogResult.Yes Then
- Exit Sub
+ If dlgres = Windows.Forms.DialogResult.Yes Then
+ Call Me.LoadFromClipboard_TALSIM(clipboardtext)
End If
- Call Me.LoadFromClipboard_TALSIM(clipboardtext)
Else
- 'ask the user whether to attempt plain text import
+ 'ask the user whether to attempt CSV import
dlgres = MessageBox.Show("Attempt to load clipboard text content in Wave as CSV data?", "Load from clipboard", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
- If Not dlgres = Windows.Forms.DialogResult.Yes Then
- Exit Sub
+ If dlgres = Windows.Forms.DialogResult.Yes Then
+ 'save as temp text file and then load file
+ Dim tmpfile As String = IO.Path.GetTempFileName()
+ Using writer As New IO.StreamWriter(tmpfile, False, Helpers.DefaultEncoding)
+ writer.Write(clipboardtext)
+ End Using
+ Call Me.Import_File(tmpfile)
+ 'delete temp file after import
+ IO.File.Delete(tmpfile)
End If
- 'save as temp text file and then load file
- Dim tmpfile As String = IO.Path.GetTempFileName()
- Using writer As New IO.StreamWriter(tmpfile, False, Helpers.DefaultEncoding)
- writer.Write(clipboardtext)
- End Using
- Call Me.Import_File(tmpfile)
- 'delete temp file after import
- IO.File.Delete(tmpfile)
End If
Else
MessageBox.Show("No usable clipboard content detected!", "Load from clipboard", MessageBoxButtons.OK, MessageBoxIcon.Error)
From 974172cb5d890b521038cefc01be2d995e989661 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Sat, 14 Mar 2026 17:15:07 +0100
Subject: [PATCH 06/22] remove call to WEL.extractFromWLZIP from
TimeSeriesFile, as it is now handled by TalsimClipboard
---
source/Classes/TimeSeriesFile.vb | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/source/Classes/TimeSeriesFile.vb b/source/Classes/TimeSeriesFile.vb
index 480b17d5..ef841db9 100644
--- a/source/Classes/TimeSeriesFile.vb
+++ b/source/Classes/TimeSeriesFile.vb
@@ -664,14 +664,7 @@ Public MustInherit Class TimeSeriesFile
'Check whether the file exists
If Not IO.File.Exists(file) Then
- 'A WEL/WBL file may be zipped within a WLZIP file, so try extracting it from there
- If fileExt = FileExtensions.WEL Or fileExt = FileExtensions.WBL Then
- If Not Fileformats.WEL.extractFromWLZIP(file) Then
- Throw New IO.FileNotFoundException($"File '{file}' not found!")
- End If
- Else
- Throw New IO.FileNotFoundException($"File '{file}' not found!")
- End If
+ Throw New IO.FileNotFoundException($"File '{file}' not found!")
End If
'set default
From b74dc4f5b5d9bc02a067f72f5a92bb7aa104f4f1 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Sat, 14 Mar 2026 17:48:08 +0100
Subject: [PATCH 07/22] overload Parser.Process in TalsimClipboard so that we
can delete any WEL files that we extract from WLZIP files after they have
been processed #136
---
source/Parsers/TalsimClipboard.vb | 39 +++++++++++++++++++++++++------
1 file changed, 32 insertions(+), 7 deletions(-)
diff --git a/source/Parsers/TalsimClipboard.vb b/source/Parsers/TalsimClipboard.vb
index a17bb950..bf5ab30f 100644
--- a/source/Parsers/TalsimClipboard.vb
+++ b/source/Parsers/TalsimClipboard.vb
@@ -162,13 +162,6 @@ Namespace Parsers
name = params("Kennung").PadRight(4, " ") & "_" & params("Zustand")
End If
- If Not IO.File.Exists(file) Then
- 'A WEL/WBL file may be zipped within a WLZIP file, so try extracting it from there
- If Fileformats.WEL.extractFromWLZIP(file) Then
- 'TODO #136: we should ideally clean up the extracted file later
- End If
- End If
-
'convert interpretation value to string
Dim interpretationValue As Integer = Integer.Parse(params("Interpretation"))
Dim interpretationString As String = [Enum].GetName(GetType(TimeSeries.InterpretationEnum), interpretationValue)
@@ -216,6 +209,38 @@ Namespace Parsers
End Sub
+ Public Overloads Function Process() As List(Of TimeSeries)
+
+ 'check for WEL/WBL that do not exist and try to extract them from WLZIP files if possible
+ Dim extractedFiles As New List(Of String)
+ For Each fileRef As FileReference In FileReferences
+ If Not IO.File.Exists(fileRef.path) Then
+ Dim fileExt As String = IO.Path.GetExtension(fileRef.path).ToUpper()
+ If fileExt = TimeSeriesFile.FileExtensions.WEL Or fileExt = TimeSeriesFile.FileExtensions.WBL Then
+ If Fileformats.WEL.extractFromWLZIP(fileRef.path) Then
+ 'remember the file so that we can delete it later
+ extractedFiles.Add(fileRef.path)
+ End If
+ End If
+ End If
+ Next
+
+ 'process the files as usual
+ Dim tsList As List(Of TimeSeries) = MyBase.Process()
+
+ 'delete any extracted files
+ For Each file As String In extractedFiles
+ Try
+ IO.File.Delete(file)
+ Catch ex As Exception
+ 'ignore any errors during deletion
+ End Try
+ Next
+
+ Return tsList
+
+ End Function
+
End Class
End Namespace
\ No newline at end of file
From a4ad6813c36ff1d8bf1de3659e5450ec52d3a64b Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Sat, 14 Mar 2026 13:17:58 +0100
Subject: [PATCH 08/22] move Talsim-specific tests to a separate class and add
a new test TestWLZIPFromClipboard
---
source/Wave.vb | 2 +-
tests/TestImport.vb | 22 ------------
tests/TestTalsim.vb | 79 +++++++++++++++++++++++++++++++++++++++++
tests/Wave.Tests.vbproj | 1 +
4 files changed, 81 insertions(+), 23 deletions(-)
create mode 100644 tests/TestTalsim.vb
diff --git a/source/Wave.vb b/source/Wave.vb
index df376953..e513210a 100644
--- a/source/Wave.vb
+++ b/source/Wave.vb
@@ -276,7 +276,7 @@ Public Class Wave
'''
''' text content of the clipboard
'''
- Private Sub LoadFromClipboard_TALSIM(clipboardtext As String)
+ Public Sub LoadFromClipboard_TALSIM(clipboardtext As String)
Try
Call Log.AddLogEntry(Log.levels.info, $"Parsing Talsim clipboard content...")
diff --git a/tests/TestImport.vb b/tests/TestImport.vb
index 67c49151..a03c04ab 100644
--- a/tests/TestImport.vb
+++ b/tests/TestImport.vb
@@ -24,28 +24,6 @@ Imports Microsoft.VisualStudio.TestTools.UnitTesting
Public Class TestImport
- '''
- ''' Tests finding a WEL file within a WLZIP file and extracting it
- '''
-
- Public Sub TestWLZIP()
-
- Dim file_wel As String = IO.Path.Combine(TestData.getTestDataDir(), "Talsim", "TALSIM.WEL")
-
- 'delete existing file before testing
- If IO.File.Exists(file_wel) Then
- IO.File.Delete(file_wel)
- End If
-
- 'attempt to extract from WLZIP
- Dim success As Boolean = Fileformats.WEL.extractFromWLZIP(file_wel)
-
- Assert.IsTrue(success)
- Assert.IsTrue(IO.File.Exists(file_wel))
-
- End Sub
-
-
'''
''' Tests importing all supported time series file formats
'''
diff --git a/tests/TestTalsim.vb b/tests/TestTalsim.vb
new file mode 100644
index 00000000..4d1981bd
--- /dev/null
+++ b/tests/TestTalsim.vb
@@ -0,0 +1,79 @@
+'BlueM.Wave
+'Copyright (C) BlueM Dev Group
+'
+'
+'This program is free software: you can redistribute it and/or modify
+'it under the terms of the GNU Lesser General Public License as published by
+'the Free Software Foundation, either version 3 of the License, or
+'(at your option) any later version.
+'
+'This program is distributed in the hope that it will be useful,
+'but WITHOUT ANY WARRANTY; without even the implied warranty of
+'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+'GNU Lesser General Public License for more details.
+'
+'You should have received a copy of the GNU Lesser General Public License
+'along with this program. If not, see .
+'
+Imports System.Text
+Imports Microsoft.VisualStudio.TestTools.UnitTesting
+
+'''
+''' Tests Talsim-specific functionality
+'''
+
+Public Class TestTalsim
+
+ '''
+ ''' Tests finding a WEL file within a WLZIP file and extracting it
+ '''
+
+ Public Sub TestWLZIP()
+
+ Dim file_wel As String = IO.Path.Combine(TestData.getTestDataDir(), "Talsim", "TALSIM.WEL")
+
+ 'delete existing file before testing
+ If IO.File.Exists(file_wel) Then
+ IO.File.Delete(file_wel)
+ End If
+
+ 'attempt to extract from WLZIP
+ Dim success As Boolean = Fileformats.WEL.extractFromWLZIP(file_wel)
+
+ Assert.IsTrue(success)
+ Assert.IsTrue(IO.File.Exists(file_wel))
+
+ 'clean up by deleting the extracted file
+ IO.File.Delete(file_wel)
+
+ End Sub
+
+
+ '''
+ ''' Tests loading a WEL file within a WLZIP file when processing Talsim clipboard data
+ '''
+
+ Public Sub TestWLZIPFromTalsimClipboard()
+
+ Dim file_clipboard As String = IO.Path.Combine(TestData.getTestDataDir(), "Talsim", "Clipboard_Talsim_WLZIP.txt")
+ Dim file_wel As String = IO.Path.Combine(TestData.getTestDataDir(), "Talsim", "TALSIM.WEL")
+
+ 'delete existing file before testing
+ If IO.File.Exists(file_wel) Then
+ IO.File.Delete(file_wel)
+ End If
+
+ Dim clipboardData As String = IO.File.ReadAllText(file_clipboard)
+
+ Dim wave As New BlueM.Wave.Wave()
+ wave.LoadFromClipboard_TALSIM(clipboardData)
+
+ 'check that time series was loaded into app
+ Assert.IsTrue(wave.TimeSeries.Count > 0)
+
+ 'check that extracted file was deleted
+ Assert.IsFalse(IO.File.Exists(file_wel))
+
+ End Sub
+
+End Class
\ No newline at end of file
diff --git a/tests/Wave.Tests.vbproj b/tests/Wave.Tests.vbproj
index 75eff3b0..c69e658c 100644
--- a/tests/Wave.Tests.vbproj
+++ b/tests/Wave.Tests.vbproj
@@ -92,6 +92,7 @@
+
From 602727c2cb7f203283ee6069b64e4b3f8b4e6f11 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Sat, 14 Mar 2026 18:06:09 +0100
Subject: [PATCH 09/22] move extractFromWLZIP method to TalsimClipboard
---
source/FileFormats/WEL.vb | 50 -----------------------------
source/Parsers/TalsimClipboard.vb | 52 ++++++++++++++++++++++++++++++-
tests/TestTalsim.vb | 2 +-
3 files changed, 52 insertions(+), 52 deletions(-)
diff --git a/source/FileFormats/WEL.vb b/source/FileFormats/WEL.vb
index e8080b3c..2dd1edc3 100644
--- a/source/FileFormats/WEL.vb
+++ b/source/FileFormats/WEL.vb
@@ -310,56 +310,6 @@ Namespace Fileformats
End Function
- '''
- ''' Attempts to extract a specified WEL file from a WLZIP file of the same name
- '''
- ''' path to WEL file
- ''' True if successful
- ''' TALSIM specific
- Public Shared Function extractFromWLZIP(file As String) As Boolean
-
- Dim file_wlzip As String
- Dim filename As String
- Dim zipEntryFound As Boolean = False
- Dim success As Boolean = False
-
- 'determine WLZIP filename for files ending with .WEL (may also be e.g. .KTR.WEL, .CHLO.WEL, etc.)
- Dim m As Match = Regex.Match(file, "^(.+?)(\.[a-z]+)?\.WEL$", RegexOptions.IgnoreCase)
- If m.Success Then
- file_wlzip = $"{m.Groups(1)}.WLZIP"
-
- If IO.File.Exists(file_wlzip) Then
-
- Log.AddLogEntry(Log.levels.info, $"Looking for file in {file_wlzip} ...")
- filename = IO.Path.GetFileName(file)
-
- Dim zip As IO.Compression.ZipArchive = IO.Compression.ZipFile.OpenRead(file_wlzip)
-
- For Each entry As IO.Compression.ZipArchiveEntry In zip.Entries
- If entry.Name.ToLower() = filename.ToLower() Then
- zipEntryFound = True
- 'extract file from zip archive
- Log.AddLogEntry(Log.levels.info, $"Extracting file from {file_wlzip} ...")
- Dim fs As New IO.FileStream(file, FileMode.CreateNew)
- entry.Open().CopyTo(fs)
- fs.Flush()
- fs.Close()
- success = True
- Exit For
- End If
- Next
-
- If Not zipEntryFound Then
- Log.AddLogEntry(Log.levels.error, $"File {filename} not found in {file_wlzip}!")
- End If
-
- End If
- End If
-
- Return success
-
- End Function
-
#End Region 'Methoden
End Class
diff --git a/source/Parsers/TalsimClipboard.vb b/source/Parsers/TalsimClipboard.vb
index bf5ab30f..82f83d40 100644
--- a/source/Parsers/TalsimClipboard.vb
+++ b/source/Parsers/TalsimClipboard.vb
@@ -217,7 +217,7 @@ Namespace Parsers
If Not IO.File.Exists(fileRef.path) Then
Dim fileExt As String = IO.Path.GetExtension(fileRef.path).ToUpper()
If fileExt = TimeSeriesFile.FileExtensions.WEL Or fileExt = TimeSeriesFile.FileExtensions.WBL Then
- If Fileformats.WEL.extractFromWLZIP(fileRef.path) Then
+ If TalsimClipboard.extractFromWLZIP(fileRef.path) Then
'remember the file so that we can delete it later
extractedFiles.Add(fileRef.path)
End If
@@ -241,6 +241,56 @@ Namespace Parsers
End Function
+ '''
+ ''' Attempts to extract a specified WEL file from a WLZIP file of the same name
+ '''
+ ''' path to WEL file
+ ''' True if successful
+ ''' TALSIM specific
+ Public Shared Function extractFromWLZIP(file As String) As Boolean
+
+ Dim file_wlzip As String
+ Dim filename As String
+ Dim zipEntryFound As Boolean = False
+ Dim success As Boolean = False
+
+ 'determine WLZIP filename for files ending with .WEL (may also be e.g. .KTR.WEL, .CHLO.WEL, etc.)
+ Dim m As Match = Regex.Match(file, "^(.+?)(\.[a-z]+)?\.WEL$", RegexOptions.IgnoreCase)
+ If m.Success Then
+ file_wlzip = $"{m.Groups(1)}.WLZIP"
+
+ If IO.File.Exists(file_wlzip) Then
+
+ Log.AddLogEntry(Log.levels.info, $"Looking for file in {file_wlzip} ...")
+ filename = IO.Path.GetFileName(file)
+
+ Dim zip As IO.Compression.ZipArchive = IO.Compression.ZipFile.OpenRead(file_wlzip)
+
+ For Each entry As IO.Compression.ZipArchiveEntry In zip.Entries
+ If entry.Name.ToLower() = filename.ToLower() Then
+ zipEntryFound = True
+ 'extract file from zip archive
+ Log.AddLogEntry(Log.levels.info, $"Extracting file from {file_wlzip} ...")
+ Dim fs As New IO.FileStream(file, IO.FileMode.CreateNew)
+ entry.Open().CopyTo(fs)
+ fs.Flush()
+ fs.Close()
+ success = True
+ Exit For
+ End If
+ Next
+
+ If Not zipEntryFound Then
+ Log.AddLogEntry(Log.levels.error, $"File {filename} not found in {file_wlzip}!")
+ End If
+
+ End If
+ End If
+
+ Return success
+
+ End Function
+
End Class
End Namespace
\ No newline at end of file
diff --git a/tests/TestTalsim.vb b/tests/TestTalsim.vb
index 4d1981bd..5e0b1ae1 100644
--- a/tests/TestTalsim.vb
+++ b/tests/TestTalsim.vb
@@ -38,7 +38,7 @@ Public Class TestTalsim
End If
'attempt to extract from WLZIP
- Dim success As Boolean = Fileformats.WEL.extractFromWLZIP(file_wel)
+ Dim success As Boolean = Parsers.TalsimClipboard.extractFromWLZIP(file_wel)
Assert.IsTrue(success)
Assert.IsTrue(IO.File.Exists(file_wel))
From c130e17fe51a2a170aed24dec1a3a1b0a96cc6ae Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Sat, 14 Mar 2026 18:45:04 +0100
Subject: [PATCH 10/22] update changelog
---
source/CHANGELOG.md | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/source/CHANGELOG.md b/source/CHANGELOG.md
index d0b24602..4154da71 100644
--- a/source/CHANGELOG.md
+++ b/source/CHANGELOG.md
@@ -3,7 +3,14 @@ BlueM.Wave Release Notes
Version X.X.X
-------------
+FIXED:
+* WEL files extracted from WLZIP archives when processing Talsim clipboard data are now deleted after use #136
+
+CHANGED:
+* Automatic extraction of WEL files from WLZIP archives now only occurs when processing Talsim clipboard data
+
API-CHANGES:
+* New namespace `Parsers` containing classes for parsing file import instructions such as WVP files and Talsim clipboard content
* Renamed `TimeSeriesFile.Write_File()` functions to `writeFile()` for consistency
Version 2.16.0
From 1811845f8fd96e9388ed8b731324edcbb9d3f1e1 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 11:05:35 +0100
Subject: [PATCH 11/22] simplify by reusing TimeSeriesDisplayOptions class
---
source/Parsers/Parser.vb | 31 ++++++++++---------------------
source/Parsers/WVP.vb | 22 +++++++---------------
2 files changed, 17 insertions(+), 36 deletions(-)
diff --git a/source/Parsers/Parser.vb b/source/Parsers/Parser.vb
index b171db8b..5b704e1f 100644
--- a/source/Parsers/Parser.vb
+++ b/source/Parsers/Parser.vb
@@ -54,15 +54,15 @@ Namespace Parsers
'''
''' Holds options for importing a series, e.g. display options, custom title and unit, etc.
'''
- Protected Structure SeriesOptions
- Dim title As String
- Dim unit As String
- Dim color As String
- Dim linestyle As String
- Dim linewidth As String
- Dim interpretation As String
- Dim showpoints As String
- End Structure
+ Protected Class SeriesOptions
+ Public title As String
+ Public unit As String
+ Public interpretation As String
+ Public displayOptions As TimeSeriesDisplayOptions
+ Public Sub New()
+ Me.displayOptions = New TimeSeriesDisplayOptions()
+ End Sub
+ End Class
'''
''' The path to the input file
@@ -212,18 +212,7 @@ Namespace Parsers
End If
End If
'display options
- If Not IsNothing(options.color) Then
- ts.DisplayOptions.SetColor(options.color)
- End If
- If Not IsNothing(options.linestyle) Then
- ts.DisplayOptions.SetLineStyle(options.linestyle)
- End If
- If Not IsNothing(options.linewidth) Then
- ts.DisplayOptions.SetLineWidth(options.linewidth)
- End If
- If Not IsNothing(options.showpoints) Then
- ts.DisplayOptions.SetShowPoints(options.showpoints)
- End If
+ ts.DisplayOptions = options.displayOptions
End If
'store the time series in the list
tsList.Add(ts)
diff --git a/source/Parsers/WVP.vb b/source/Parsers/WVP.vb
index bcf475d2..38970734 100644
--- a/source/Parsers/WVP.vb
+++ b/source/Parsers/WVP.vb
@@ -98,15 +98,7 @@ Namespace Parsers
If m.Success Then
seriesName = m.Groups("name").Value.Trim()
'check for additional series options
- 'by default, series options are nothing
- Dim options As New SeriesOptions With {
- .title = Nothing,
- .unit = Nothing,
- .color = Nothing,
- .linestyle = Nothing,
- .linewidth = Nothing,
- .interpretation = Nothing
- }
+ Dim options As New SeriesOptions()
If m.Groups("options").Success Then
'parse series options
Dim optionString As String = m.Groups("optionstring").Value.Trim()
@@ -126,16 +118,16 @@ Namespace Parsers
options.title = value
Case "unit"
options.unit = value
+ Case "interpretation"
+ options.interpretation = value
Case "color"
- options.color = value
+ options.displayOptions.SetColor(value)
Case "linestyle"
- options.linestyle = value
+ options.displayOptions.SetLineStyle(value)
Case "linewidth"
- options.linewidth = value
- Case "interpretation"
- options.interpretation = value
+ options.displayOptions.SetLineWidth(value)
Case "showpoints"
- options.showpoints = value
+ options.displayOptions.SetShowPoints(value)
Case Else
Log.AddLogEntry(levels.warning, $"Series import option keyword {keyword} not recognized!")
End Select
From a77110d19918a2294bcd89d5467cf2f743945546 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 11:20:04 +0100
Subject: [PATCH 12/22] centralize parsing of interpretation from string and
integer
---
source/Helpers.vb | 30 ++++++++++++++++++++++++++++++
source/Parsers/Parser.vb | 10 +++-------
source/Parsers/TalsimClipboard.vb | 23 +++++++++++------------
source/Parsers/WVP.vb | 2 +-
4 files changed, 45 insertions(+), 20 deletions(-)
diff --git a/source/Helpers.vb b/source/Helpers.vb
index fad32966..9f1e30b9 100644
--- a/source/Helpers.vb
+++ b/source/Helpers.vb
@@ -123,6 +123,36 @@ Public Module Helpers
End Function
+ '''
+ ''' Parses a string to a TimeSeries.InterpretationEnum value.
+ ''' If the string does not match any of the enum names, TimeSeries.InterpretationEnum.Undefined is returned and a warning is logged.
+ '''
+ ''' The string to be parsed
+ ''' The corresponding TimeSeries.InterpretationEnum value
+ Public Function ParseInterpretation(interpretationString As String) As TimeSeries.InterpretationEnum
+ If Not [Enum].IsDefined(GetType(TimeSeries.InterpretationEnum), interpretationString) Then
+ Log.AddLogEntry(levels.warning, $"Interpretation {interpretationString} is not recognized!")
+ Return TimeSeries.InterpretationEnum.Undefined
+ Else
+ Return [Enum].Parse(GetType(TimeSeries.InterpretationEnum), interpretationString)
+ End If
+ End Function
+
+ '''
+ ''' Parses an integer to a TimeSeries.InterpretationEnum value.
+ ''' If the integer does not match any of the enum values, TimeSeries.InterpretationEnum.Undefined is returned and a warning is logged.
+ '''
+ ''' The integer value to be parsed
+ ''' The corresponding TimeSeries.InterpretationEnum value
+ Public Function ParseInterpretation(interpretationValue As Integer) As TimeSeries.InterpretationEnum
+ If Not [Enum].IsDefined(GetType(TimeSeries.InterpretationEnum), interpretationValue) Then
+ Log.AddLogEntry(levels.warning, $"Interpretation {interpretationValue} is not recognized!")
+ Return TimeSeries.InterpretationEnum.Undefined
+ Else
+ Return [Enum].Parse(GetType(TimeSeries.InterpretationEnum), interpretationValue)
+ End If
+ End Function
+
'''
''' Returns a specified color palette
'''
diff --git a/source/Parsers/Parser.vb b/source/Parsers/Parser.vb
index 5b704e1f..301c3fb3 100644
--- a/source/Parsers/Parser.vb
+++ b/source/Parsers/Parser.vb
@@ -57,7 +57,7 @@ Namespace Parsers
Protected Class SeriesOptions
Public title As String
Public unit As String
- Public interpretation As String
+ Public interpretation As TimeSeries.InterpretationEnum?
Public displayOptions As TimeSeriesDisplayOptions
Public Sub New()
Me.displayOptions = New TimeSeriesDisplayOptions()
@@ -204,12 +204,8 @@ Namespace Parsers
If Not IsNothing(options.unit) Then
ts.Unit = options.unit
End If
- If Not IsNothing(options.interpretation) Then
- If Not [Enum].IsDefined(GetType(TimeSeries.InterpretationEnum), options.interpretation) Then
- Log.AddLogEntry(levels.warning, $"Interpretation {options.interpretation} is not recognized!")
- Else
- ts.Interpretation = [Enum].Parse(GetType(TimeSeries.InterpretationEnum), options.interpretation)
- End If
+ If options.interpretation.HasValue Then
+ ts.Interpretation = options.interpretation
End If
'display options
ts.DisplayOptions = options.displayOptions
diff --git a/source/Parsers/TalsimClipboard.vb b/source/Parsers/TalsimClipboard.vb
index 82f83d40..c3f1577f 100644
--- a/source/Parsers/TalsimClipboard.vb
+++ b/source/Parsers/TalsimClipboard.vb
@@ -162,14 +162,15 @@ Namespace Parsers
name = params("Kennung").PadRight(4, " ") & "_" & params("Zustand")
End If
- 'convert interpretation value to string
- Dim interpretationValue As Integer = Integer.Parse(params("Interpretation"))
- Dim interpretationString As String = [Enum].GetName(GetType(TimeSeries.InterpretationEnum), interpretationValue)
+ Dim options As New SeriesOptions() With {
+ .title = name,
+ .interpretation = Helpers.ParseInterpretation(Integer.Parse(params("Interpretation")))
+ }
Dim fileRef As New FileReference() With {
.path = file,
.series = New Dictionary(Of String, SeriesOptions)() From {
- {name, New SeriesOptions() With {.interpretation = interpretationString}}
+ {name, options}
}
}
FileReferences.Add(fileRef)
@@ -182,19 +183,17 @@ Namespace Parsers
name = params("Kennung")
- 'convert interpretation value to string
- Dim interpretationValue As Integer = Integer.Parse(params("Interpretation"))
- Dim interpretationString As String = [Enum].GetName(GetType(TimeSeries.InterpretationEnum), interpretationValue)
+ Dim options As New SeriesOptions() With {
+ .title = name,
+ .unit = params("Einheit"),
+ .interpretation = Helpers.ParseInterpretation(Integer.Parse(params("Interpretation")))
+ }
Dim fileRef As New FileReference() With {
.path = file,
.series = New Dictionary(Of String, SeriesOptions)() From {
{
- IO.Path.GetFileName(file), New SeriesOptions() With {
- .title = name,
- .unit = params("Einheit"),
- .interpretation = interpretationString
- }
+ IO.Path.GetFileName(file), options
}
}
}
diff --git a/source/Parsers/WVP.vb b/source/Parsers/WVP.vb
index 38970734..ec7103de 100644
--- a/source/Parsers/WVP.vb
+++ b/source/Parsers/WVP.vb
@@ -119,7 +119,7 @@ Namespace Parsers
Case "unit"
options.unit = value
Case "interpretation"
- options.interpretation = value
+ options.interpretation = Helpers.ParseInterpretation(value)
Case "color"
options.displayOptions.SetColor(value)
Case "linestyle"
From 430913e00dca2e7600a63f7ac50f9729c77b040e Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 11:57:20 +0100
Subject: [PATCH 13/22] check for optional "Einheit" entry
---
source/Parsers/TalsimClipboard.vb | 3 +++
1 file changed, 3 insertions(+)
diff --git a/source/Parsers/TalsimClipboard.vb b/source/Parsers/TalsimClipboard.vb
index c3f1577f..55e85f95 100644
--- a/source/Parsers/TalsimClipboard.vb
+++ b/source/Parsers/TalsimClipboard.vb
@@ -166,6 +166,9 @@ Namespace Parsers
.title = name,
.interpretation = Helpers.ParseInterpretation(Integer.Parse(params("Interpretation")))
}
+ If params.ContainsKey("Einheit") Then
+ options.unit = params("Einheit")
+ End If
Dim fileRef As New FileReference() With {
.path = file,
From a1d4562cc7167a4ca6454b4c6cd49834ced123e4 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 11:57:41 +0100
Subject: [PATCH 14/22] add more tests for Talsim clipboard
---
tests/TestTalsim.vb | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/tests/TestTalsim.vb b/tests/TestTalsim.vb
index 5e0b1ae1..18e7e9a4 100644
--- a/tests/TestTalsim.vb
+++ b/tests/TestTalsim.vb
@@ -24,6 +24,25 @@ Imports Microsoft.VisualStudio.TestTools.UnitTesting
Public Class TestTalsim
+
+
+
+
+
+
+
+ Public Sub TestTestTalsimClipboard(clipboardFile As String)
+
+ Dim clipboardData As String = IO.File.ReadAllText(IO.Path.Combine(TestData.getTestDataDir(), clipboardFile))
+
+ Dim wave As New BlueM.Wave.Wave()
+ wave.LoadFromClipboard_TALSIM(clipboardData)
+
+ 'check that time series were imported
+ Assert.IsTrue(wave.TimeSeries.Count > 0)
+
+ End Sub
+
'''
''' Tests finding a WEL file within a WLZIP file and extracting it
'''
@@ -68,7 +87,7 @@ Public Class TestTalsim
Dim wave As New BlueM.Wave.Wave()
wave.LoadFromClipboard_TALSIM(clipboardData)
- 'check that time series was loaded into app
+ 'check that time series was imported
Assert.IsTrue(wave.TimeSeries.Count > 0)
'check that extracted file was deleted
From f4d855ad1c6b9d30babd756194d735339ba7679b Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 12:06:32 +0100
Subject: [PATCH 15/22] add docstring and warning log entry on failure to
delete extracted file #136
---
source/Parsers/TalsimClipboard.vb | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/source/Parsers/TalsimClipboard.vb b/source/Parsers/TalsimClipboard.vb
index 55e85f95..cd639b61 100644
--- a/source/Parsers/TalsimClipboard.vb
+++ b/source/Parsers/TalsimClipboard.vb
@@ -211,6 +211,12 @@ Namespace Parsers
End Sub
+ '''
+ ''' Wraps the base Process() method to check for WEL/WBL files that do not exist
+ ''' and attempt to extract them from WLZIP files if possible before processing,
+ ''' and then deletes any extracted files after processing
+ '''
+ ''' A list of processed time series
Public Overloads Function Process() As List(Of TimeSeries)
'check for WEL/WBL that do not exist and try to extract them from WLZIP files if possible
@@ -235,7 +241,7 @@ Namespace Parsers
Try
IO.File.Delete(file)
Catch ex As Exception
- 'ignore any errors during deletion
+ Log.AddLogEntry(Log.levels.warning, $"Could not delete extracted file {file}: {ex.Message}")
End Try
Next
From a80c94c0bb0807bf4c71eaa06b00925099be636e Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 12:06:46 +0100
Subject: [PATCH 16/22] update changelog
---
source/CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/source/CHANGELOG.md b/source/CHANGELOG.md
index 4154da71..c870175c 100644
--- a/source/CHANGELOG.md
+++ b/source/CHANGELOG.md
@@ -11,6 +11,7 @@ CHANGED:
API-CHANGES:
* New namespace `Parsers` containing classes for parsing file import instructions such as WVP files and Talsim clipboard content
+* New helper method `Helpers.ParseInterpretation()` for parsing interpretation from strings and integers
* Renamed `TimeSeriesFile.Write_File()` functions to `writeFile()` for consistency
Version 2.16.0
From 95fc2bce92ebb4a7ede9aa4f8eea79821dd7bf54 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 16:11:18 +0100
Subject: [PATCH 17/22] rename test method
---
tests/TestTalsim.vb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/TestTalsim.vb b/tests/TestTalsim.vb
index 18e7e9a4..97db44fc 100644
--- a/tests/TestTalsim.vb
+++ b/tests/TestTalsim.vb
@@ -31,7 +31,7 @@ Public Class TestTalsim
- Public Sub TestTestTalsimClipboard(clipboardFile As String)
+ Public Sub TestTalsimClipboard(clipboardFile As String)
Dim clipboardData As String = IO.File.ReadAllText(IO.Path.Combine(TestData.getTestDataDir(), clipboardFile))
From db4bff0b1075d4c833c5559973d54dde5726cf8f Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 16:34:04 +0100
Subject: [PATCH 18/22] upload test results even if tests fail
---
.github/workflows/run-tests.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 234ad500..e0fb6d7f 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -37,6 +37,7 @@ jobs:
dotnet test BlueM.Wave\tests\bin\x64\Debug\Wave.Tests.dll --settings BlueM.Wave\tests\tests.runsettings
- name: Upload test results
+ if: always()
uses: actions/upload-artifact@v6
with:
name: test-results
From e17dd37073d07edcade91db6c61a891496d8b6bc Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 17:38:47 +0100
Subject: [PATCH 19/22] more robust splitting of lines in clipboard text
---
source/Parsers/TalsimClipboard.vb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/Parsers/TalsimClipboard.vb b/source/Parsers/TalsimClipboard.vb
index cd639b61..7e5b15ff 100644
--- a/source/Parsers/TalsimClipboard.vb
+++ b/source/Parsers/TalsimClipboard.vb
@@ -102,7 +102,7 @@ Namespace Parsers
Dim file, name As String
zreblock = False
- For Each line As String In InputText.Split(eol)
+ For Each line As String In InputText.Split(New String() {vbCr, vbLf}, StringSplitOptions.RemoveEmptyEntries)
line = line.Trim()
From 81c8ad2a9aa6cb77f13585fd416d0116b6053db9 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 17:39:09 +0100
Subject: [PATCH 20/22] make LoadFromClipboard_TALSIM private again
---
source/Wave.vb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source/Wave.vb b/source/Wave.vb
index e513210a..df376953 100644
--- a/source/Wave.vb
+++ b/source/Wave.vb
@@ -276,7 +276,7 @@ Public Class Wave
'''
''' text content of the clipboard
'''
- Public Sub LoadFromClipboard_TALSIM(clipboardtext As String)
+ Private Sub LoadFromClipboard_TALSIM(clipboardtext As String)
Try
Call Log.AddLogEntry(Log.levels.info, $"Parsing Talsim clipboard content...")
From 31a1cc7f9e3cc03c4dbf9e577420490abfdc39e4 Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 17:44:00 +0100
Subject: [PATCH 21/22] do not instantiate a Wave instance when testing as that
can cause a messagebox to appear and cause the test to hang
---
tests/TestTalsim.vb | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/tests/TestTalsim.vb b/tests/TestTalsim.vb
index 97db44fc..5bb81b81 100644
--- a/tests/TestTalsim.vb
+++ b/tests/TestTalsim.vb
@@ -35,11 +35,11 @@ Public Class TestTalsim
Dim clipboardData As String = IO.File.ReadAllText(IO.Path.Combine(TestData.getTestDataDir(), clipboardFile))
- Dim wave As New BlueM.Wave.Wave()
- wave.LoadFromClipboard_TALSIM(clipboardData)
+ Dim talsimclipboard As New Parsers.TalsimClipboard(clipboardData)
+ Dim tsList As List(Of TimeSeries) = talsimclipboard.Process()
- 'check that time series were imported
- Assert.IsTrue(wave.TimeSeries.Count > 0)
+ 'check that time series were created
+ Assert.IsTrue(tsList.Count > 0)
End Sub
@@ -84,11 +84,11 @@ Public Class TestTalsim
Dim clipboardData As String = IO.File.ReadAllText(file_clipboard)
- Dim wave As New BlueM.Wave.Wave()
- wave.LoadFromClipboard_TALSIM(clipboardData)
+ Dim talsimclipboard As New Parsers.TalsimClipboard(clipboardData)
+ Dim tsList As List(Of TimeSeries) = talsimclipboard.Process()
- 'check that time series was imported
- Assert.IsTrue(wave.TimeSeries.Count > 0)
+ 'check that time series were created
+ Assert.IsTrue(tsList.Count > 0)
'check that extracted file was deleted
Assert.IsFalse(IO.File.Exists(file_wel))
From b5d3cf9a776cbca2f0305b3cee58095a549b4cca Mon Sep 17 00:00:00 2001
From: jamaa <90166+jamaa@users.noreply.github.com>
Date: Fri, 20 Mar 2026 17:44:34 +0100
Subject: [PATCH 22/22] change workdir in Talsim tests to allow for use of
relative paths in test clipboard files
---
tests/TestData.vb | 21 ++++++++++-----
tests/TestTalsim.vb | 62 ++++++++++++++++++++++++++++++++-------------
2 files changed, 58 insertions(+), 25 deletions(-)
diff --git a/tests/TestData.vb b/tests/TestData.vb
index de6ab3d4..fcd1cbdd 100644
--- a/tests/TestData.vb
+++ b/tests/TestData.vb
@@ -20,19 +20,26 @@
'''
Module TestData
+ '''
+ ''' Returns the absolute path to the parent directory of the repository
+ ''' by going up 5 levels from the application directory
+ '''
+ Friend Function getParentDir() As String
+ Dim appdir As String = My.Application.Info.DirectoryPath() 'e.g. BlueM.Wave\tests\bin\x64\Debug
+ Dim rootdir As String = appdir
+ For i As Integer = 1 To 5
+ rootdir = IO.Directory.GetParent(rootdir).FullName
+ Next
+ Return rootdir
+ End Function
+
'''
''' Returns the absolute path to the test data directory BlueM.Datasets\Wave
''' which is expected to be in the same directory as BlueM.Wave
'''
- '''
Friend Function getTestDataDir() As String
Try
- Dim appdir As String = My.Application.Info.DirectoryPath() 'e.g. BlueM.Wave\tests\bin\x64\Debug
- Dim testdatadir As String = appdir
- For i As Integer = 1 To 5
- testdatadir = IO.Directory.GetParent(testdatadir).FullName
- Next
- testdatadir = IO.Path.Combine(testdatadir, "BlueM.Datasets", "Wave")
+ Dim testdatadir As String = IO.Path.Combine(getParentDir(), "BlueM.Datasets", "Wave")
If Not IO.Directory.Exists(testdatadir) Then
Throw New AssertInconclusiveException($"Directory {testdatadir} does not exist.")
End If
diff --git a/tests/TestTalsim.vb b/tests/TestTalsim.vb
index 5bb81b81..c9dc1a7e 100644
--- a/tests/TestTalsim.vb
+++ b/tests/TestTalsim.vb
@@ -33,13 +33,26 @@ Public Class TestTalsim
Public Sub TestTalsimClipboard(clipboardFile As String)
- Dim clipboardData As String = IO.File.ReadAllText(IO.Path.Combine(TestData.getTestDataDir(), clipboardFile))
+ Dim workdir = IO.Directory.GetCurrentDirectory()
+ Try
+ 'set current directory to test assemply path to ensure any relative paths in the clipboard data are correct
+ IO.Directory.SetCurrentDirectory(My.Application.Info.DirectoryPath())
- Dim talsimclipboard As New Parsers.TalsimClipboard(clipboardData)
- Dim tsList As List(Of TimeSeries) = talsimclipboard.Process()
+ Dim clipboardData As String = IO.File.ReadAllText(IO.Path.Combine(TestData.getTestDataDir(), clipboardFile))
- 'check that time series were created
- Assert.IsTrue(tsList.Count > 0)
+ Dim talsimclipboard As New Parsers.TalsimClipboard(clipboardData)
+ Dim tsList As List(Of TimeSeries) = talsimclipboard.Process()
+
+ 'check that time series were created
+ Assert.IsTrue(tsList.Count > 0)
+
+ Catch ex As Exception
+ Assert.Fail("Exception occurred: " & ex.Message)
+
+ Finally
+ 'restore original working directory
+ IO.Directory.SetCurrentDirectory(workdir)
+ End Try
End Sub
@@ -74,24 +87,37 @@ Public Class TestTalsim
Public Sub TestWLZIPFromTalsimClipboard()
- Dim file_clipboard As String = IO.Path.Combine(TestData.getTestDataDir(), "Talsim", "Clipboard_Talsim_WLZIP.txt")
- Dim file_wel As String = IO.Path.Combine(TestData.getTestDataDir(), "Talsim", "TALSIM.WEL")
+ Dim workdir = IO.Directory.GetCurrentDirectory()
+ Try
+ 'set current directory to test assemply path to ensure any relative paths in the clipboard data are correct
+ IO.Directory.SetCurrentDirectory(My.Application.Info.DirectoryPath())
- 'delete existing file before testing
- If IO.File.Exists(file_wel) Then
- IO.File.Delete(file_wel)
- End If
+ Dim file_clipboard As String = IO.Path.Combine(TestData.getTestDataDir(), "Talsim", "Clipboard_Talsim_WLZIP.txt")
+ Dim file_wel As String = IO.Path.Combine(TestData.getTestDataDir(), "Talsim", "TALSIM.WEL")
+
+ 'delete existing file before testing
+ If IO.File.Exists(file_wel) Then
+ IO.File.Delete(file_wel)
+ End If
+
+ Dim clipboardData As String = IO.File.ReadAllText(file_clipboard)
+
+ Dim talsimclipboard As New Parsers.TalsimClipboard(clipboardData)
+ Dim tsList As List(Of TimeSeries) = talsimclipboard.Process()
- Dim clipboardData As String = IO.File.ReadAllText(file_clipboard)
+ 'check that time series were created
+ Assert.IsTrue(tsList.Count > 0)
- Dim talsimclipboard As New Parsers.TalsimClipboard(clipboardData)
- Dim tsList As List(Of TimeSeries) = talsimclipboard.Process()
+ 'check that extracted file was deleted
+ Assert.IsFalse(IO.File.Exists(file_wel))
- 'check that time series were created
- Assert.IsTrue(tsList.Count > 0)
+ Catch ex As Exception
+ Assert.Fail("Exception occurred: " & ex.Message)
- 'check that extracted file was deleted
- Assert.IsFalse(IO.File.Exists(file_wel))
+ Finally
+ 'restore original working directory
+ IO.Directory.SetCurrentDirectory(workdir)
+ End Try
End Sub