Files
LMEMoxaTransfer/TrasferimentoProgrammiOkuma.App/Form1.vb
T

1245 lines
55 KiB
VB.net

Imports System.IO
Imports System.Net.Sockets
Imports System.Reflection
Imports System.Security.Cryptography
Imports System.Text
Imports System.Text.Json
Imports System.Threading
Public Class Form1
Private Const ConfigFileName As String = "moxa-okuma-settings.json"
Private ReadOnly cboAzienda As New ComboBox()
Private ReadOnly cboMacchina As New ComboBox()
Private ReadOnly txtIpMoxa As New TextBox()
Private ReadOnly nudPorta As New NumericUpDown()
Private ReadOnly nudTimeoutConnessione As New NumericUpDown()
Private ReadOnly nudTimeoutInattivita As New NumericUpDown()
Private ReadOnly nudPausaRiga As New NumericUpDown()
Private ReadOnly nudPausaCarattere As New NumericUpDown()
Private ReadOnly nudDimensioneBlocco As New NumericUpDown()
Private ReadOnly nudPausaBlocco As New NumericUpDown()
Private ReadOnly nudAttesaPrimaInvio As New NumericUpDown()
Private ReadOnly nudAttesaDopoInvio As New NumericUpDown()
Private ReadOnly chkInvioRigaPerRiga As New CheckBox()
Private ReadOnly chkInvioCaratterePerCarattere As New CheckBox()
Private ReadOnly chkInvioBlocchi As New CheckBox()
Private ReadOnly cboTerminatoreInvio As New ComboBox()
Private ReadOnly txtFileInvio As New TextBox()
Private ReadOnly txtFileRicezione As New TextBox()
Private ReadOnly btnSfogliaInvio As New Button()
Private ReadOnly btnSfogliaRicezione As New Button()
Private ReadOnly btnTestConnessione As New Button()
Private ReadOnly btnInvia As New Button()
Private ReadOnly btnRicevi As New Button()
Private ReadOnly btnAnnulla As New Button()
Private ReadOnly mnuSblocca As New ToolStripMenuItem("Sblocca controlli")
Private ReadOnly mnuCambiaPassword As New ToolStripMenuItem("Cambia password")
Private ReadOnly mnuPulisciLog As New ToolStripMenuItem("Pulisci log")
Private ReadOnly mnuInformazioni As New ToolStripMenuItem("Informazioni su")
Private ReadOnly btnAggiornaStatoMoxa As New Button()
Private ReadOnly chkMonitorStatoMoxa As New CheckBox()
Private ReadOnly nudIntervalloStatoMoxa As New NumericUpDown()
Private ReadOnly lstStatoMoxa As New ListView()
Private ReadOnly imgStatoMoxa As New ImageList()
Private ReadOnly statusTimer As New System.Windows.Forms.Timer()
Private ReadOnly txtLog As New TextBox()
Private ReadOnly progress As New ProgressBar()
Private ReadOnly lockedControls As New List(Of Control)()
Private appConfig As AppConfig
Private cts As CancellationTokenSource
Private controlsUnlocked As Boolean
Private loadingSelection As Boolean
Private checkingMoxaStatus As Boolean
Public Sub New()
InitializeComponent()
appConfig = LoadOrCreateConfig()
CleanupOldTemporaryReceives()
BuildInterface()
LoadCompanies()
SetControlsUnlocked(False)
End Sub
Private Sub BuildInterface()
Text = "LMEMoxaTransfer"
Icon = New Icon(Path.Combine(Application.StartupPath, "Assets", "app-icon.ico"))
MinimumSize = New Size(1030, 690)
Size = New Size(1120, 760)
StartPosition = FormStartPosition.CenterScreen
Dim shell As New TableLayoutPanel With {
.Dock = DockStyle.Fill,
.ColumnCount = 1,
.RowCount = 2,
.Margin = Padding.Empty,
.Padding = Padding.Empty
}
shell.RowStyles.Add(New RowStyle(SizeType.AutoSize))
shell.RowStyles.Add(New RowStyle(SizeType.Percent, 100))
Controls.Add(shell)
Dim mainMenu = BuildMainMenu()
MainMenuStrip = mainMenu
mainMenu.Dock = DockStyle.Fill
shell.Controls.Add(mainMenu, 0, 0)
Dim tabs As New TabControl With {.Dock = DockStyle.Fill, .Margin = Padding.Empty}
Dim tabTrasferimento As New TabPage("Trasferimento programmi")
Dim tabStatoMoxa As New TabPage("Stato Moxa")
tabs.TabPages.Add(tabTrasferimento)
tabs.TabPages.Add(tabStatoMoxa)
shell.Controls.Add(tabs, 0, 1)
Dim root As New TableLayoutPanel With {
.Dock = DockStyle.Fill,
.ColumnCount = 1,
.RowCount = 4,
.Padding = New Padding(14)
}
root.RowStyles.Add(New RowStyle(SizeType.AutoSize))
root.RowStyles.Add(New RowStyle(SizeType.AutoSize))
root.RowStyles.Add(New RowStyle(SizeType.Percent, 100))
root.RowStyles.Add(New RowStyle(SizeType.AutoSize))
tabTrasferimento.Controls.Add(root)
Dim grpConnessione As New GroupBox With {
.Text = "Connessione Moxa",
.Dock = DockStyle.Top,
.AutoSize = True,
.Padding = New Padding(12)
}
root.Controls.Add(grpConnessione, 0, 0)
Dim conn As New TableLayoutPanel With {.Dock = DockStyle.Fill, .ColumnCount = 10, .AutoSize = True}
conn.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
conn.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 150))
conn.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
conn.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 230))
conn.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
conn.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 135))
conn.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
conn.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 90))
conn.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
conn.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 90))
grpConnessione.Controls.Add(conn)
cboAzienda.DropDownStyle = ComboBoxStyle.DropDownList
cboAzienda.Dock = DockStyle.Fill
cboMacchina.DropDownStyle = ComboBoxStyle.DropDownList
cboMacchina.Dock = DockStyle.Fill
txtIpMoxa.Dock = DockStyle.Fill
txtIpMoxa.ReadOnly = True
nudPorta.Minimum = 1
nudPorta.Maximum = 65535
nudTimeoutConnessione.Minimum = 1
nudTimeoutConnessione.Maximum = 120
nudTimeoutInattivita.Minimum = 1
nudTimeoutInattivita.Maximum = 120
conn.Controls.Add(New Label With {.Text = "Azienda", .AutoSize = True, .Anchor = AnchorStyles.Left}, 0, 0)
conn.Controls.Add(cboAzienda, 1, 0)
conn.Controls.Add(New Label With {.Text = "Macchina", .AutoSize = True, .Anchor = AnchorStyles.Left, .Margin = New Padding(14, 0, 3, 0)}, 2, 0)
conn.Controls.Add(cboMacchina, 3, 0)
conn.Controls.Add(New Label With {.Text = "IP Moxa", .AutoSize = True, .Anchor = AnchorStyles.Left, .Margin = New Padding(14, 0, 3, 0)}, 4, 0)
conn.Controls.Add(txtIpMoxa, 5, 0)
conn.Controls.Add(New Label With {.Text = "Porta", .AutoSize = True, .Anchor = AnchorStyles.Left, .Margin = New Padding(14, 0, 3, 0)}, 6, 0)
conn.Controls.Add(nudPorta, 7, 0)
conn.Controls.Add(New Label With {.Text = "Timeout s", .AutoSize = True, .Anchor = AnchorStyles.Left, .Margin = New Padding(14, 0, 3, 0)}, 8, 0)
conn.Controls.Add(nudTimeoutConnessione, 9, 0)
Dim grpTrasferimento As New GroupBox With {
.Text = "Invio e ricezione programmi",
.Dock = DockStyle.Top,
.AutoSize = True,
.Padding = New Padding(12),
.Margin = New Padding(0, 12, 0, 12)
}
root.Controls.Add(grpTrasferimento, 0, 1)
Dim transfer As New TableLayoutPanel With {.Dock = DockStyle.Fill, .ColumnCount = 4, .RowCount = 10, .AutoSize = True}
transfer.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
transfer.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100))
transfer.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
transfer.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 120))
grpTrasferimento.Controls.Add(transfer)
ConfigureButton(btnSfogliaInvio, "Sfoglia")
ConfigureButton(btnSfogliaRicezione, "Sfoglia")
ConfigureButton(btnTestConnessione, "Test connessione")
ConfigureButton(btnInvia, "Invia a Okuma")
ConfigureButton(btnRicevi, "Ricevi da Okuma")
ConfigureButton(btnAnnulla, "Annulla")
btnAnnulla.Enabled = False
txtFileInvio.Dock = DockStyle.Fill
txtFileRicezione.Dock = DockStyle.Fill
cboTerminatoreInvio.DropDownStyle = ComboBoxStyle.DropDownList
cboTerminatoreInvio.Items.AddRange(New Object() {"Nessuno", "CR", "LF", "CRLF", "%", "% + CRLF"})
chkInvioRigaPerRiga.Text = "Invio riga per riga"
chkInvioRigaPerRiga.AutoSize = True
chkInvioCaratterePerCarattere.Text = "Invio carattere per carattere"
chkInvioCaratterePerCarattere.AutoSize = True
chkInvioBlocchi.Text = "Invio a blocchi (% + CRLF)"
chkInvioBlocchi.AutoSize = True
ConfigureNumericDefaults()
transfer.Controls.Add(New Label With {.Text = "File da inviare", .AutoSize = True, .Anchor = AnchorStyles.Left}, 0, 0)
transfer.Controls.Add(txtFileInvio, 1, 0)
transfer.Controls.Add(btnSfogliaInvio, 2, 0)
transfer.Controls.Add(btnInvia, 3, 0)
transfer.Controls.Add(New Label With {.Text = "Cartella ricezione", .AutoSize = True, .Anchor = AnchorStyles.Left}, 0, 1)
transfer.Controls.Add(txtFileRicezione, 1, 1)
transfer.Controls.Add(btnSfogliaRicezione, 2, 1)
transfer.Controls.Add(btnRicevi, 3, 1)
transfer.Controls.Add(New Label With {.Text = "Terminatore invio", .AutoSize = True, .Anchor = AnchorStyles.Left}, 0, 2)
transfer.Controls.Add(cboTerminatoreInvio, 1, 2)
transfer.Controls.Add(btnAnnulla, 3, 2)
transfer.Controls.Add(New Label With {.Text = "Pausa riga ms", .AutoSize = True, .Anchor = AnchorStyles.Left}, 0, 3)
transfer.Controls.Add(nudPausaRiga, 1, 3)
transfer.Controls.Add(chkInvioRigaPerRiga, 2, 3)
transfer.Controls.Add(New Label With {.Text = "Pausa carattere ms", .AutoSize = True, .Anchor = AnchorStyles.Left}, 0, 4)
transfer.Controls.Add(nudPausaCarattere, 1, 4)
transfer.Controls.Add(chkInvioCaratterePerCarattere, 2, 4)
transfer.Controls.Add(New Label With {.Text = "Modo Okuma READ", .AutoSize = True, .Anchor = AnchorStyles.Left}, 0, 5)
transfer.Controls.Add(chkInvioBlocchi, 1, 5)
transfer.Controls.Add(New Label With {.Text = "Dim. blocco byte", .AutoSize = True, .Anchor = AnchorStyles.Left}, 2, 5)
transfer.Controls.Add(nudDimensioneBlocco, 3, 5)
transfer.Controls.Add(New Label With {.Text = "Pausa blocco ms", .AutoSize = True, .Anchor = AnchorStyles.Left}, 0, 6)
transfer.Controls.Add(nudPausaBlocco, 1, 6)
transfer.Controls.Add(New Label With {.Text = "Fine ricez. s", .AutoSize = True, .Anchor = AnchorStyles.Left}, 2, 6)
transfer.Controls.Add(nudTimeoutInattivita, 3, 6)
transfer.Controls.Add(New Label With {.Text = "Attesa prima invio s", .AutoSize = True, .Anchor = AnchorStyles.Left}, 0, 7)
transfer.Controls.Add(nudAttesaPrimaInvio, 1, 7)
transfer.Controls.Add(btnTestConnessione, 3, 7)
transfer.Controls.Add(New Label With {.Text = "Attesa dopo invio s", .AutoSize = True, .Anchor = AnchorStyles.Left}, 0, 8)
transfer.Controls.Add(nudAttesaDopoInvio, 1, 8)
Dim hint As New Label With {
.Text = "Prima di ricevere o inviare, selezionare azienda e macchina. I parametri Moxa vengono compilati dal file JSON nello start path.",
.AutoSize = True,
.Dock = DockStyle.Fill,
.ForeColor = Color.FromArgb(70, 70, 70),
.Margin = New Padding(0, 10, 0, 0)
}
transfer.SetColumnSpan(hint, 4)
transfer.Controls.Add(hint, 0, 9)
txtLog.Dock = DockStyle.Fill
txtLog.Multiline = True
txtLog.ScrollBars = ScrollBars.Vertical
txtLog.ReadOnly = True
txtLog.Font = New Font("Consolas", 10.0F)
root.Controls.Add(txtLog, 0, 2)
progress.Dock = DockStyle.Fill
progress.Style = ProgressBarStyle.Blocks
root.Controls.Add(progress, 0, 3)
lockedControls.AddRange({nudPorta, nudTimeoutConnessione, nudTimeoutInattivita, cboTerminatoreInvio, nudPausaRiga, nudPausaCarattere, nudDimensioneBlocco, nudPausaBlocco, nudAttesaPrimaInvio, nudAttesaDopoInvio, chkInvioRigaPerRiga, chkInvioCaratterePerCarattere, chkInvioBlocchi})
AddHandler cboAzienda.SelectedIndexChanged, AddressOf CompanyChanged
AddHandler cboMacchina.SelectedIndexChanged, AddressOf MachineChanged
AddHandler mnuSblocca.Click, AddressOf UnlockClicked
AddHandler mnuCambiaPassword.Click, AddressOf ChangePasswordClicked
AddHandler mnuPulisciLog.Click, AddressOf ClearLogClicked
AddHandler mnuInformazioni.Click, AddressOf AboutClicked
AddHandler btnSfogliaInvio.Click, AddressOf SelectInputFile
AddHandler btnSfogliaRicezione.Click, AddressOf SelectOutputFile
AddHandler btnTestConnessione.Click, AddressOf TestConnectionClicked
AddHandler btnInvia.Click, AddressOf SendClicked
AddHandler btnRicevi.Click, AddressOf ReceiveClicked
AddHandler btnAnnulla.Click, Sub() cts?.Cancel()
txtFileRicezione.Text = GetInitialReceiveDirectory()
BuildStatusTab(tabStatoMoxa)
Log($"Pronto. Configurazione: {ConfigPath()}")
End Sub
Private Function BuildMainMenu() As MenuStrip
Dim menu As New MenuStrip()
Dim strumenti As New ToolStripMenuItem("Strumenti")
strumenti.DropDownItems.Add(mnuSblocca)
strumenti.DropDownItems.Add(mnuCambiaPassword)
strumenti.DropDownItems.Add(New ToolStripSeparator())
strumenti.DropDownItems.Add(mnuPulisciLog)
Dim aiuto As New ToolStripMenuItem("?")
aiuto.DropDownItems.Add(mnuInformazioni)
menu.Items.Add(strumenti)
menu.Items.Add(aiuto)
Return menu
End Function
Private Function GetInitialReceiveDirectory() As String
If Not String.IsNullOrWhiteSpace(appConfig.LastReceiveDirectory) AndAlso Directory.Exists(appConfig.LastReceiveDirectory) Then
Return appConfig.LastReceiveDirectory
End If
Return Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
End Function
Private Sub BuildStatusTab(tab As TabPage)
Dim root As New TableLayoutPanel With {
.Dock = DockStyle.Fill,
.ColumnCount = 1,
.RowCount = 2,
.Padding = New Padding(14)
}
root.RowStyles.Add(New RowStyle(SizeType.AutoSize))
root.RowStyles.Add(New RowStyle(SizeType.Percent, 100))
tab.Controls.Add(root)
Dim actions As New FlowLayoutPanel With {.Dock = DockStyle.Fill, .AutoSize = True, .FlowDirection = FlowDirection.LeftToRight}
ConfigureButton(btnAggiornaStatoMoxa, "Aggiorna")
chkMonitorStatoMoxa.Text = "Controllo automatico"
chkMonitorStatoMoxa.AutoSize = True
chkMonitorStatoMoxa.Checked = True
chkMonitorStatoMoxa.Margin = New Padding(16, 8, 0, 3)
nudIntervalloStatoMoxa.Minimum = 5
nudIntervalloStatoMoxa.Maximum = 3600
nudIntervalloStatoMoxa.Value = Math.Max(nudIntervalloStatoMoxa.Minimum, Math.Min(nudIntervalloStatoMoxa.Maximum, appConfig.MonitorRefreshSeconds))
nudIntervalloStatoMoxa.Width = 80
actions.Controls.Add(btnAggiornaStatoMoxa)
actions.Controls.Add(chkMonitorStatoMoxa)
actions.Controls.Add(New Label With {.Text = "Intervallo s", .AutoSize = True, .Margin = New Padding(16, 9, 3, 3)})
actions.Controls.Add(nudIntervalloStatoMoxa)
root.Controls.Add(actions, 0, 0)
imgStatoMoxa.ImageSize = New Size(16, 16)
imgStatoMoxa.ColorDepth = ColorDepth.Depth32Bit
imgStatoMoxa.Images.Add("unknown", CreateStatusIcon(Color.Gray))
imgStatoMoxa.Images.Add("online", CreateStatusIcon(Color.FromArgb(0, 150, 70)))
imgStatoMoxa.Images.Add("offline", CreateStatusIcon(Color.FromArgb(210, 40, 40)))
lstStatoMoxa.Dock = DockStyle.Fill
lstStatoMoxa.View = View.Details
lstStatoMoxa.FullRowSelect = True
lstStatoMoxa.GridLines = True
lstStatoMoxa.SmallImageList = imgStatoMoxa
lstStatoMoxa.Columns.Add("Macchina", 280)
lstStatoMoxa.Columns.Add("IP Moxa", 140)
lstStatoMoxa.Columns.Add("Porta", 70)
lstStatoMoxa.Columns.Add("Stato", 130)
lstStatoMoxa.Columns.Add("Ultimo controllo", 160)
lstStatoMoxa.Columns.Add("Note", 220)
root.Controls.Add(lstStatoMoxa, 0, 1)
lockedControls.Add(chkMonitorStatoMoxa)
lockedControls.Add(nudIntervalloStatoMoxa)
statusTimer.Interval = CInt(nudIntervalloStatoMoxa.Value) * 1000
statusTimer.Enabled = chkMonitorStatoMoxa.Checked
AddHandler btnAggiornaStatoMoxa.Click, AddressOf RefreshMoxaStatusClicked
AddHandler statusTimer.Tick, AddressOf StatusTimerTick
AddHandler nudIntervalloStatoMoxa.ValueChanged, AddressOf StatusIntervalChanged
AddHandler chkMonitorStatoMoxa.CheckedChanged, AddressOf StatusMonitorChanged
PopulateMoxaStatusList()
End Sub
Private Shared Function CreateStatusIcon(color As Color) As Bitmap
Dim bitmap As New Bitmap(16, 16)
Using g = Graphics.FromImage(bitmap)
g.Clear(Color.Transparent)
Using brush As New SolidBrush(color)
g.FillEllipse(brush, 2, 2, 12, 12)
End Using
Using pen As New Pen(Color.FromArgb(80, 0, 0, 0))
g.DrawEllipse(pen, 2, 2, 12, 12)
End Using
End Using
Return bitmap
End Function
Private Sub ConfigureNumericDefaults()
nudPorta.Minimum = 1
nudPorta.Maximum = 65535
nudTimeoutConnessione.Minimum = 1
nudTimeoutConnessione.Maximum = 120
nudTimeoutInattivita.Minimum = 1
nudTimeoutInattivita.Maximum = 120
nudPausaRiga.Minimum = 0
nudPausaRiga.Maximum = 2000
nudPausaCarattere.Minimum = 0
nudPausaCarattere.Maximum = 200
nudDimensioneBlocco.Minimum = 1
nudDimensioneBlocco.Maximum = 1024
nudPausaBlocco.Minimum = 0
nudPausaBlocco.Maximum = 5000
nudAttesaPrimaInvio.Minimum = 0
nudAttesaPrimaInvio.Maximum = 60
nudAttesaDopoInvio.Minimum = 0
nudAttesaDopoInvio.Maximum = 30
End Sub
Private Sub LoadCompanies()
loadingSelection = True
cboAzienda.Items.Clear()
For Each company In appConfig.Companies
cboAzienda.Items.Add(company.Name)
Next
loadingSelection = False
If cboAzienda.Items.Count > 0 Then
Dim index = Math.Max(0, appConfig.Companies.FindIndex(Function(c) c.Name = appConfig.DefaultCompany))
cboAzienda.SelectedIndex = index
End If
PopulateMoxaStatusList()
End Sub
Private Sub PopulateMoxaStatusList()
If lstStatoMoxa.Columns.Count = 0 Then
Return
End If
lstStatoMoxa.BeginUpdate()
lstStatoMoxa.Items.Clear()
lstStatoMoxa.Groups.Clear()
For Each company In appConfig.Companies
Dim group As New ListViewGroup(company.Name, HorizontalAlignment.Left)
lstStatoMoxa.Groups.Add(group)
For Each machine In company.Machines
Dim item As New ListViewItem(machine.Name, "unknown") With {
.Group = group,
.Tag = New MoxaStatusTarget With {.Company = company.Name, .Machine = machine}
}
item.SubItems.Add(machine.IpMoxa)
item.SubItems.Add(appConfig.DefaultSettings.Port.ToString())
item.SubItems.Add("Non controllato")
item.SubItems.Add("")
item.SubItems.Add(machine.Note)
lstStatoMoxa.Items.Add(item)
Next
Next
lstStatoMoxa.EndUpdate()
End Sub
Private Sub CompanyChanged(sender As Object, e As EventArgs)
If loadingSelection OrElse cboAzienda.SelectedIndex < 0 Then
Return
End If
Dim company = CurrentCompany()
loadingSelection = True
cboMacchina.Items.Clear()
For Each machine In company.Machines
cboMacchina.Items.Add(machine.Name)
Next
loadingSelection = False
If cboMacchina.Items.Count > 0 Then
cboMacchina.SelectedIndex = 0
End If
End Sub
Private Sub MachineChanged(sender As Object, e As EventArgs)
If loadingSelection Then
Return
End If
ApplyCurrentMachineSettings()
End Sub
Private Sub ApplyCurrentMachineSettings()
Dim machine = CurrentMachine()
If machine Is Nothing Then
Return
End If
Dim defaults = appConfig.DefaultSettings
txtIpMoxa.Text = machine.IpMoxa
nudPorta.Value = defaults.Port
nudTimeoutConnessione.Value = defaults.ConnectionTimeoutSeconds
nudTimeoutInattivita.Value = defaults.ReceiveIdleTimeoutSeconds
cboTerminatoreInvio.SelectedItem = defaults.SendTerminator
nudPausaRiga.Value = defaults.LineDelayMs
nudPausaCarattere.Value = defaults.CharDelayMs
chkInvioRigaPerRiga.Checked = defaults.LinePaced
chkInvioCaratterePerCarattere.Checked = defaults.CharPaced
chkInvioBlocchi.Checked = defaults.BlockPaced
nudDimensioneBlocco.Value = defaults.BlockSizeBytes
nudPausaBlocco.Value = defaults.BlockDelayMs
nudAttesaPrimaInvio.Value = defaults.WaitBeforeSendSeconds
nudAttesaDopoInvio.Value = defaults.WaitAfterSendSeconds
Log($"Macchina selezionata: {CurrentCompany().Name} / {machine.Name} - IP Moxa {machine.IpMoxa}.")
End Sub
Private Function CurrentCompany() As CompanyConfig
If cboAzienda.SelectedIndex < 0 OrElse cboAzienda.SelectedIndex >= appConfig.Companies.Count Then
Return Nothing
End If
Return appConfig.Companies(cboAzienda.SelectedIndex)
End Function
Private Function CurrentMachine() As MachineConfig
Dim company = CurrentCompany()
If company Is Nothing OrElse cboMacchina.SelectedIndex < 0 OrElse cboMacchina.SelectedIndex >= company.Machines.Count Then
Return Nothing
End If
Return company.Machines(cboMacchina.SelectedIndex)
End Function
Private Sub UnlockClicked(sender As Object, e As EventArgs)
If controlsUnlocked Then
SetControlsUnlocked(False)
Return
End If
If String.IsNullOrWhiteSpace(appConfig.AdminPasswordHash) OrElse String.IsNullOrWhiteSpace(appConfig.AdminPasswordSalt) Then
Dim newPassword = PromptPassword("Password non configurata", "Inserire una nuova password amministratore:")
If String.IsNullOrEmpty(newPassword) Then
Return
End If
SetPassword(newPassword)
SaveConfig()
SetControlsUnlocked(True)
Return
End If
Dim password = PromptPassword("Sblocca controlli", "Inserire la password:")
If password Is Nothing Then
Return
End If
If VerifyPassword(password) Then
SetControlsUnlocked(True)
Else
MessageBox.Show(Me, "Password non corretta.", "Accesso negato", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End If
End Sub
Private Sub ChangePasswordClicked(sender As Object, e As EventArgs)
If Not String.IsNullOrWhiteSpace(appConfig.AdminPasswordHash) AndAlso Not String.IsNullOrWhiteSpace(appConfig.AdminPasswordSalt) Then
Dim oldPassword = PromptPassword("Cambia password", "Inserire la vecchia password:")
If oldPassword Is Nothing Then
Return
End If
If Not VerifyPassword(oldPassword) Then
MessageBox.Show(Me, "Vecchia password non corretta.", "Password", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return
End If
End If
Dim newPassword = PromptPassword("Cambia password", "Inserire la nuova password:")
If String.IsNullOrEmpty(newPassword) Then
Return
End If
SetPassword(newPassword)
SaveConfig()
MessageBox.Show(Me, "Password aggiornata.", "Password", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
Private Sub ClearLogClicked(sender As Object, e As EventArgs)
txtLog.Clear()
End Sub
Private Sub AboutClicked(sender As Object, e As EventArgs)
Dim version = Assembly.GetExecutingAssembly().GetName().Version
MessageBox.Show(Me,
$"LMEMoxaTransfer{Environment.NewLine}{Environment.NewLine}Azienda: Compomac SPA{Environment.NewLine}Autore: Livio Merola{Environment.NewLine}Versione: {version}",
"Informazioni su",
MessageBoxButtons.OK,
MessageBoxIcon.Information)
End Sub
Private Async Sub RefreshMoxaStatusClicked(sender As Object, e As EventArgs)
Await RefreshMoxaStatusAsync()
End Sub
Private Async Sub StatusTimerTick(sender As Object, e As EventArgs)
If chkMonitorStatoMoxa.Checked Then
Await RefreshMoxaStatusAsync()
End If
End Sub
Private Sub StatusIntervalChanged(sender As Object, e As EventArgs)
statusTimer.Interval = CInt(nudIntervalloStatoMoxa.Value) * 1000
appConfig.MonitorRefreshSeconds = CInt(nudIntervalloStatoMoxa.Value)
SaveConfig()
End Sub
Private Sub StatusMonitorChanged(sender As Object, e As EventArgs)
statusTimer.Enabled = chkMonitorStatoMoxa.Checked
End Sub
Private Async Function RefreshMoxaStatusAsync() As Task
If checkingMoxaStatus Then
Return
End If
checkingMoxaStatus = True
btnAggiornaStatoMoxa.Enabled = False
Try
For Each item As ListViewItem In lstStatoMoxa.Items
Dim target = TryCast(item.Tag, MoxaStatusTarget)
If target Is Nothing Then
Continue For
End If
item.ImageKey = "unknown"
item.SubItems(3).Text = "Controllo..."
item.SubItems(4).Text = DateTime.Now.ToString("HH:mm:ss")
Dim online = Await IsMoxaReachableAsync(target.Machine.IpMoxa, appConfig.DefaultSettings.Port, TimeSpan.FromMilliseconds(1200))
item.ImageKey = If(online, "online", "offline")
item.SubItems(3).Text = If(online, "Raggiungibile", "Non raggiungibile")
item.SubItems(4).Text = DateTime.Now.ToString("HH:mm:ss")
Next
Finally
btnAggiornaStatoMoxa.Enabled = True
checkingMoxaStatus = False
End Try
End Function
Private Shared Async Function IsMoxaReachableAsync(ip As String, port As Integer, timeout As TimeSpan) As Task(Of Boolean)
If String.IsNullOrWhiteSpace(ip) Then
Return False
End If
Using client As New TcpClient()
Try
Dim connectTask = client.ConnectAsync(ip.Trim(), port)
Dim completed = Await Task.WhenAny(connectTask, Task.Delay(timeout))
If completed IsNot connectTask Then
Return False
End If
Await connectTask
Return True
Catch
Return False
End Try
End Using
End Function
Private Sub SetControlsUnlocked(unlocked As Boolean)
controlsUnlocked = unlocked
For Each control In lockedControls
control.Enabled = unlocked
Next
mnuSblocca.Text = If(unlocked, "Blocca controlli", "Sblocca controlli")
End Sub
Private Async Sub TestConnectionClicked(sender As Object, e As EventArgs)
Await RunTransferAsync(Async Function(token)
Using client = Await ConnectAsync(token)
Log("Test OK: connessione TCP aperta, nessun dato inviato a Okuma.")
End Using
End Function)
End Sub
Private Shared Sub ConfigureButton(button As Button, text As String)
button.Text = text
button.AutoSize = True
button.MinimumSize = New Size(110, 32)
button.Margin = New Padding(8, 3, 0, 3)
End Sub
Private Sub SelectInputFile(sender As Object, e As EventArgs)
Using dlg As New OpenFileDialog()
dlg.Title = "Seleziona programma da inviare a Okuma"
dlg.Filter = "Programmi CNC|*.min;*.eia;*.iso;*.txt;*.nc|Tutti i file|*.*"
If dlg.ShowDialog(Me) = DialogResult.OK Then
txtFileInvio.Text = dlg.FileName
End If
End Using
End Sub
Private Sub SelectOutputFile(sender As Object, e As EventArgs)
Using dlg As New FolderBrowserDialog()
dlg.Description = "Scegli la cartella dove salvare il programma ricevuto"
dlg.UseDescriptionForTitle = True
If Directory.Exists(txtFileRicezione.Text) Then
dlg.SelectedPath = txtFileRicezione.Text
End If
If dlg.ShowDialog(Me) = DialogResult.OK Then
txtFileRicezione.Text = dlg.SelectedPath
appConfig.LastReceiveDirectory = dlg.SelectedPath
SaveConfig()
End If
End Using
End Sub
Private Async Sub SendClicked(sender As Object, e As EventArgs)
If CurrentMachine() Is Nothing Then
MessageBox.Show(Me, "Selezionare azienda e macchina.", "Macchina mancante", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return
End If
If Not File.Exists(txtFileInvio.Text) Then
MessageBox.Show(Me, "Selezionare un file esistente da inviare.", "File mancante", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return
End If
Await RunTransferAsync(Async Function(token)
Dim fileBytes = Await File.ReadAllBytesAsync(txtFileInvio.Text, token)
fileBytes = RemoveUtf8BomIfPresent(fileBytes)
LogFirstBytes(fileBytes)
LogNonStandardCncBytes(fileBytes)
Dim blockPaced = chkInvioBlocchi.Checked
Dim terminator = If(blockPaced, Encoding.ASCII.GetBytes("%" & vbCrLf), GetTerminatorBytes())
Using client = Await ConnectAsync(token)
Using stream = client.GetStream()
Dim waitBeforeSend = CInt(nudAttesaPrimaInvio.Value)
If waitBeforeSend > 0 Then
Log($"Attesa prima del primo byte: {waitBeforeSend} s.")
Await Task.Delay(TimeSpan.FromSeconds(waitBeforeSend), token)
End If
If blockPaced Then
Dim blockSize = CInt(nudDimensioneBlocco.Value)
Dim blockDelayMs = CInt(nudPausaBlocco.Value)
Log($"Invio Okuma READ: blocchi da {blockSize} byte con pausa {blockDelayMs} ms.")
Await SendBlockPacedAsync(stream, fileBytes, blockSize, blockDelayMs, token)
ElseIf chkInvioRigaPerRiga.Checked Then
Await SendLinePacedAsync(stream, fileBytes, CInt(nudPausaRiga.Value), CInt(nudPausaCarattere.Value), chkInvioCaratterePerCarattere.Checked, token)
Else
Await stream.WriteAsync(fileBytes, token)
End If
If terminator.Length > 0 Then
Await stream.WriteAsync(terminator, token)
End If
Await stream.FlushAsync(token)
Dim waitAfterSend = CInt(nudAttesaDopoInvio.Value)
If blockPaced Then
waitAfterSend = Math.Max(waitAfterSend, 1)
End If
If waitAfterSend > 0 Then
Await Task.Delay(TimeSpan.FromSeconds(waitAfterSend), token)
End If
End Using
End Using
Log($"Inviati {fileBytes.Length:N0} byte a Okuma.")
End Function)
End Sub
Private Async Sub ReceiveClicked(sender As Object, e As EventArgs)
If CurrentMachine() Is Nothing Then
MessageBox.Show(Me, "Selezionare azienda e macchina.", "Macchina mancante", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return
End If
If String.IsNullOrWhiteSpace(txtFileRicezione.Text) Then
MessageBox.Show(Me, "Scegliere una cartella di destinazione per la ricezione.", "Cartella mancante", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return
End If
Await RunTransferAsync(Async Function(token)
Dim destinationDirectory = Path.GetFullPath(txtFileRicezione.Text)
Directory.CreateDirectory(destinationDirectory)
Dim temporaryPath = CreateTemporaryReceivePath()
Using client = Await ConnectAsync(token)
Using stream = client.GetStream()
Dim received = Await ReceiveUntilIdleAsync(stream, temporaryPath, token)
Log($"Ricevuti {received:N0} byte da Okuma.")
End Using
End Using
Dim finalPath = PromptAndSaveReceivedFile(temporaryPath, destinationDirectory)
Log($"File ricevuto salvato in: {finalPath}")
End Function)
End Sub
Private Async Function RunTransferAsync(action As Func(Of CancellationToken, Task)) As Task
SetBusy(True)
cts = New CancellationTokenSource()
Try
Await action(cts.Token)
Log("Operazione completata.")
Catch ex As OperationCanceledException
Log("Operazione annullata.")
Catch ex As Exception
Log("Errore: " & ex.Message)
MessageBox.Show(Me, ex.Message, "Errore trasferimento", MessageBoxButtons.OK, MessageBoxIcon.Error)
Finally
cts.Dispose()
cts = Nothing
SetBusy(False)
End Try
End Function
Private Shared Async Function SendLinePacedAsync(stream As NetworkStream, data As Byte(), lineDelayMs As Integer, charDelayMs As Integer, charPaced As Boolean, token As CancellationToken) As Task
Dim start = 0
For i = 0 To data.Length - 1
If data(i) = 10 Then
Await WriteChunkAsync(stream, data, start, i - start + 1, charDelayMs, charPaced, token)
Await stream.FlushAsync(token)
start = i + 1
If lineDelayMs > 0 Then
Await Task.Delay(lineDelayMs, token)
End If
End If
Next
If start < data.Length Then
Await WriteChunkAsync(stream, data, start, data.Length - start, charDelayMs, charPaced, token)
End If
End Function
Private Shared Async Function WriteChunkAsync(stream As NetworkStream, data As Byte(), offset As Integer, count As Integer, charDelayMs As Integer, charPaced As Boolean, token As CancellationToken) As Task
If Not charPaced Then
Await stream.WriteAsync(data.AsMemory(offset, count), token)
Return
End If
For i = offset To offset + count - 1
Await stream.WriteAsync(data.AsMemory(i, 1), token)
If charDelayMs > 0 Then
Await Task.Delay(charDelayMs, token)
End If
Next
End Function
Private Shared Async Function SendBlockPacedAsync(stream As NetworkStream, data As Byte(), blockSize As Integer, blockDelayMs As Integer, token As CancellationToken) As Task
Dim offset = 0
While offset < data.Length
Dim count = Math.Min(blockSize, data.Length - offset)
Await stream.WriteAsync(data.AsMemory(offset, count), token)
Await stream.FlushAsync(token)
offset += count
If offset < data.Length AndAlso blockDelayMs > 0 Then
Await Task.Delay(blockDelayMs, token)
End If
End While
End Function
Private Async Function ConnectAsync(token As CancellationToken) As Task(Of TcpClient)
Dim machine = CurrentMachine()
If machine Is Nothing Then
Throw New InvalidOperationException("Macchina non selezionata.")
End If
Dim ip = machine.IpMoxa.Trim()
Dim port = CInt(nudPorta.Value)
Dim timeout = TimeSpan.FromSeconds(CInt(nudTimeoutConnessione.Value))
Dim client As New TcpClient With {.NoDelay = True}
Log($"Connessione a {ip}:{port}...")
Dim connectTask = client.ConnectAsync(ip, port, token).AsTask()
Dim completed = Await Task.WhenAny(connectTask, Task.Delay(timeout, token))
If completed IsNot connectTask Then
client.Dispose()
Throw New TimeoutException($"Connessione non riuscita entro {timeout.TotalSeconds:N0} secondi.")
End If
Await connectTask
Log("Connessione aperta.")
Return client
End Function
Private Async Function ReceiveUntilIdleAsync(stream As NetworkStream, destinationPath As String, token As CancellationToken) As Task(Of Long)
Dim buffer(8191) As Byte
Dim total As Long = 0
Dim idleTimeout = TimeSpan.FromSeconds(CInt(nudTimeoutInattivita.Value))
Log("In attesa dati. Avviare l'invio programma dal controllo Okuma.")
Using output As New FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.Read, 8192, useAsync:=True)
Do
Dim readTask = stream.ReadAsync(buffer.AsMemory(0, buffer.Length), token).AsTask()
Dim completed = Await Task.WhenAny(readTask, Task.Delay(idleTimeout, token))
If completed IsNot readTask Then
If total = 0 Then
Throw New TimeoutException("Nessun dato ricevuto prima del timeout di inattivita.")
End If
Exit Do
End If
Dim count = Await readTask
If count = 0 Then
Exit Do
End If
Await output.WriteAsync(buffer.AsMemory(0, count), token)
total += count
progress.Value = CInt(Math.Min(progress.Maximum, total Mod progress.Maximum))
Log($"Ricevuti {total:N0} byte...")
Loop
End Using
Return total
End Function
Private Shared Function CreateTemporaryReceivePath() As String
Dim tempDirectory = Path.Combine(Application.StartupPath, "RicezioniTemp")
Directory.CreateDirectory(tempDirectory)
Return Path.Combine(tempDirectory, $"ricezione_okuma_{DateTime.Now:yyyyMMdd_HHmmss}_{Guid.NewGuid():N}.tmp")
End Function
Private Shared Sub CleanupOldTemporaryReceives()
Dim tempDirectory = Path.Combine(Application.StartupPath, "RicezioniTemp")
If Not Directory.Exists(tempDirectory) Then
Return
End If
For Each filePath In Directory.EnumerateFiles(tempDirectory, "*.tmp")
Try
If File.GetLastWriteTime(filePath) < DateTime.Now.AddDays(-7) Then
File.Delete(filePath)
End If
Catch
End Try
Next
End Sub
Private Function PromptAndSaveReceivedFile(temporaryPath As String, destinationDirectory As String) As String
If Not File.Exists(temporaryPath) Then
Return temporaryPath
End If
Dim suggestedName = ExtractProgramFileName(File.ReadAllBytes(temporaryPath))
If String.IsNullOrWhiteSpace(suggestedName) Then
suggestedName = "programma_okuma.min"
Log("Nessun nome programma preceduto da $ trovato nel file ricevuto: propongo un nome predefinito.")
End If
Dim chosenName As String = Nothing
Do
chosenName = PromptReceivedFileName(suggestedName)
If chosenName Is Nothing Then
Log($"Nome finale annullato: il file temporaneo resta in {temporaryPath}")
Return temporaryPath
End If
chosenName = Path.GetFileName(chosenName.Trim())
If Not String.IsNullOrWhiteSpace(chosenName) AndAlso chosenName.IndexOfAny(Path.GetInvalidFileNameChars()) < 0 Then
Exit Do
End If
MessageBox.Show(Me, "Nome file non valido.", "Nome file", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Loop
Dim finalPath = Path.Combine(destinationDirectory, chosenName)
If File.Exists(finalPath) Then
Dim overwrite = MessageBox.Show(Me, $"Il file {chosenName} esiste gia'. Sovrascriverlo?", "File esistente", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If overwrite <> DialogResult.Yes Then
Return PromptAndSaveReceivedFile(temporaryPath, destinationDirectory)
End If
End If
File.Move(temporaryPath, finalPath, True)
Return finalPath
End Function
Private Shared Function ExtractProgramFileName(data As Byte()) As String
Dim invalidChars = Path.GetInvalidFileNameChars()
For i = 0 To data.Length - 1
If data(i) <> AscW("$"c) Then
Continue For
End If
Dim builder As New StringBuilder()
For j = i + 1 To data.Length - 1
Dim value = data(j)
If value = 0 OrElse value = &HD OrElse value = &HA OrElse value = &H9 OrElse value = AscW("$"c) Then
Exit For
End If
If value < &H20 OrElse value > &H7E Then
Exit For
End If
Dim ch = ChrW(value)
If Array.IndexOf(invalidChars, ch) >= 0 Then
Exit For
End If
builder.Append(ch)
Next
Dim candidate = builder.ToString().Trim()
If candidate.Length > 0 AndAlso candidate.Contains("."c) Then
Return candidate
End If
Next
Return ""
End Function
Private Function PromptReceivedFileName(suggestedName As String) As String
Using form As New Form With {.Text = "Nome file ricevuto", .StartPosition = FormStartPosition.CenterParent, .FormBorderStyle = FormBorderStyle.FixedDialog, .MinimizeBox = False, .MaximizeBox = False, .ClientSize = New Size(440, 140)}
Dim label As New Label With {.Text = "Conferma o modifica il nome file da salvare:", .Left = 12, .Top = 12, .Width = 410, .AutoSize = False}
Dim textBox As New TextBox With {.Left = 12, .Top = 42, .Width = 410, .Text = suggestedName}
Dim ok As New Button With {.Text = "Salva", .Left = 256, .Top = 88, .Width = 78, .DialogResult = DialogResult.OK}
Dim cancel As New Button With {.Text = "Annulla", .Left = 344, .Top = 88, .Width = 78, .DialogResult = DialogResult.Cancel}
form.Controls.AddRange({label, textBox, ok, cancel})
form.AcceptButton = ok
form.CancelButton = cancel
textBox.SelectAll()
If form.ShowDialog(Me) = DialogResult.OK Then
Return textBox.Text
End If
End Using
Return Nothing
End Function
Private Function GetTerminatorBytes() As Byte()
Select Case CStr(cboTerminatoreInvio.SelectedItem)
Case "CR"
Return Encoding.ASCII.GetBytes(vbCr)
Case "LF"
Return Encoding.ASCII.GetBytes(vbLf)
Case "CRLF"
Return Encoding.ASCII.GetBytes(vbCrLf)
Case "%"
Return Encoding.ASCII.GetBytes("%")
Case "% + CRLF"
Return Encoding.ASCII.GetBytes("%" & vbCrLf)
Case Else
Return Array.Empty(Of Byte)()
End Select
End Function
Private Shared Function RemoveUtf8BomIfPresent(data As Byte()) As Byte()
If data.Length < 3 OrElse data(0) <> &HEF OrElse data(1) <> &HBB OrElse data(2) <> &HBF Then
Return data
End If
Dim cleanLength = data.Length - 3
If cleanLength = 0 Then
Return Array.Empty(Of Byte)()
End If
Dim clean(cleanLength - 1) As Byte
Buffer.BlockCopy(data, 3, clean, 0, clean.Length)
Return clean
End Function
Private Sub LogFirstBytes(data As Byte())
If data.Length = 0 Then
Log("File vuoto: nessun byte da inviare prima del terminatore.")
Return
End If
Dim previewLength = Math.Min(16, data.Length)
Dim preview As New StringBuilder()
For i = 0 To previewLength - 1
If i > 0 Then
preview.Append(" ")
End If
preview.Append(data(i).ToString("X2"))
Next
Log($"Primi byte inviati: {preview}")
If data(0) > &H7F Then
Log($"Attenzione: il primo byte e' > 7 bit ({data(0):X2}). Per Okuma 7E1 puo' essere un carattere non valido.")
End If
End Sub
Private Sub LogNonStandardCncBytes(data As Byte())
Dim firstInvalidPosition As Integer = -1
Dim firstInvalidValue As Byte = 0
Dim invalidCount As Integer
For i = 0 To data.Length - 1
Dim value = data(i)
Dim isAllowedControl = value = &H9 OrElse value = &HA OrElse value = &HD
Dim isPrintableAscii = value >= &H20 AndAlso value <= &H7E
If Not isAllowedControl AndAlso Not isPrintableAscii Then
invalidCount += 1
If firstInvalidPosition < 0 Then
firstInvalidPosition = i
firstInvalidValue = value
End If
End If
Next
If invalidCount > 0 Then
Log($"Avviso: trovati {invalidCount} byte di controllo/non ASCII. Primo valore 0x{firstInvalidValue:X2} alla posizione {firstInvalidPosition}. Invio comunque.")
End If
End Sub
Private Sub SetBusy(isBusy As Boolean)
btnInvia.Enabled = Not isBusy
btnRicevi.Enabled = Not isBusy
btnTestConnessione.Enabled = Not isBusy
btnSfogliaInvio.Enabled = Not isBusy
btnSfogliaRicezione.Enabled = Not isBusy
mnuSblocca.Enabled = Not isBusy
mnuCambiaPassword.Enabled = Not isBusy
mnuPulisciLog.Enabled = Not isBusy
btnAnnulla.Enabled = isBusy
If Not isBusy Then
For Each control In lockedControls
control.Enabled = controlsUnlocked
Next
Else
For Each control In lockedControls
control.Enabled = False
Next
End If
progress.Style = If(isBusy, ProgressBarStyle.Marquee, ProgressBarStyle.Blocks)
If Not isBusy Then
progress.Value = 0
End If
End Sub
Private Sub Log(message As String)
If InvokeRequired Then
BeginInvoke(Sub() Log(message))
Return
End If
txtLog.AppendText($"{DateTime.Now:HH:mm:ss} {message}{Environment.NewLine}")
End Sub
Private Shared Function ConfigPath() As String
Return Path.Combine(Application.StartupPath, ConfigFileName)
End Function
Private Function LoadOrCreateConfig() As AppConfig
Dim path = ConfigPath()
If File.Exists(path) Then
Dim loaded = JsonSerializer.Deserialize(Of AppConfig)(File.ReadAllText(path), JsonOptions())
If loaded IsNot Nothing Then
Return loaded
End If
End If
Dim created = CreateDefaultConfig()
appConfig = created
SetPassword("moxa")
SaveConfig()
Return appConfig
End Function
Private Sub SaveConfig()
File.WriteAllText(ConfigPath(), JsonSerializer.Serialize(appConfig, JsonOptions()))
End Sub
Private Shared Function JsonOptions() As JsonSerializerOptions
Return New JsonSerializerOptions With {.WriteIndented = True, .PropertyNameCaseInsensitive = True}
End Function
Private Function VerifyPassword(password As String) As Boolean
Dim salt = Convert.FromBase64String(appConfig.AdminPasswordSalt)
Dim expected = Convert.FromBase64String(appConfig.AdminPasswordHash)
Using pbkdf2 As New Rfc2898DeriveBytes(password, salt, 100000, HashAlgorithmName.SHA256)
Dim actual = pbkdf2.GetBytes(expected.Length)
Return CryptographicOperations.FixedTimeEquals(actual, expected)
End Using
End Function
Private Sub SetPassword(password As String)
Dim salt(15) As Byte
RandomNumberGenerator.Fill(salt)
Using pbkdf2 As New Rfc2898DeriveBytes(password, salt, 100000, HashAlgorithmName.SHA256)
appConfig.AdminPasswordSalt = Convert.ToBase64String(salt)
appConfig.AdminPasswordHash = Convert.ToBase64String(pbkdf2.GetBytes(32))
End Using
End Sub
Private Function PromptPassword(title As String, prompt As String) As String
Using form As New Form With {.Text = title, .StartPosition = FormStartPosition.CenterParent, .FormBorderStyle = FormBorderStyle.FixedDialog, .MinimizeBox = False, .MaximizeBox = False, .ClientSize = New Size(360, 130)}
Dim label As New Label With {.Text = prompt, .Left = 12, .Top = 12, .Width = 330, .AutoSize = False}
Dim textBox As New TextBox With {.Left = 12, .Top = 42, .Width = 330, .UseSystemPasswordChar = True}
Dim ok As New Button With {.Text = "OK", .Left = 176, .Top = 84, .Width = 78, .DialogResult = DialogResult.OK}
Dim cancel As New Button With {.Text = "Annulla", .Left = 264, .Top = 84, .Width = 78, .DialogResult = DialogResult.Cancel}
form.Controls.AddRange({label, textBox, ok, cancel})
form.AcceptButton = ok
form.CancelButton = cancel
If form.ShowDialog(Me) = DialogResult.OK Then
Return textBox.Text
End If
End Using
Return Nothing
End Function
Private Shared Function CreateDefaultConfig() As AppConfig
Return New AppConfig With {
.DefaultCompany = "SECUREX",
.MonitorRefreshSeconds = 30,
.DefaultSettings = New TransferSettings With {
.Port = 10001,
.ConnectionTimeoutSeconds = 10,
.ReceiveIdleTimeoutSeconds = 15,
.SendTerminator = "%",
.LineDelayMs = 100,
.CharDelayMs = 10,
.LinePaced = True,
.CharPaced = False,
.BlockPaced = False,
.BlockSizeBytes = 32,
.BlockDelayMs = 100,
.WaitBeforeSendSeconds = 2,
.WaitAfterSendSeconds = 2
},
.Companies = New List(Of CompanyConfig) From {
New CompanyConfig With {.Name = "SECUREX", .Machines = New List(Of MachineConfig) From {
New MachineConfig With {.Name = "M4 - N2LB 300", .IpMoxa = "192.168.0.101", .IpMachineWise = "192.168.0.214", .Note = "OSP-U100L"},
New MachineConfig With {.Name = "M5 - N3LB 300", .IpMoxa = "192.168.0.146", .IpMachineWise = "192.168.0.47", .Note = "OSP-E100L"},
New MachineConfig With {.Name = "M8 - CENTRO OKUMA", .IpMoxa = "192.168.0.113", .IpMachineWise = "192.168.0.68", .Note = "OSP-P200M"},
New MachineConfig With {.Name = "M11 - TORNIO OKUMA LB25I-N", .IpMoxa = "192.168.0.88", .IpMachineWise = "192.168.0.223"},
New MachineConfig With {.Name = "M12 - TORNIO OKUMA LB 400", .IpMoxa = "192.168.0.99", .IpMachineWise = "192.168.0.66"}
}},
New CompanyConfig With {.Name = "EMILIA", .Machines = New List(Of MachineConfig) From {
New MachineConfig With {.Name = "M1", .IpMoxa = "192.168.30.8", .IpMachineWise = "192.168.30.221"},
New MachineConfig With {.Name = "M2", .IpMoxa = "192.168.30.9", .IpMachineWise = "192.168.30.222"},
New MachineConfig With {.Name = "M3", .IpMoxa = "192.168.30.10", .IpMachineWise = "192.168.30.223"},
New MachineConfig With {.Name = "M4 - CENTRO LAVORO OKUMA", .IpMoxa = "192.168.30.11", .IpMachineWise = "192.168.30.224"},
New MachineConfig With {.Name = "M5", .IpMoxa = "192.168.30.12", .IpMachineWise = "192.168.30.225"},
New MachineConfig With {.Name = "M6", .IpMoxa = "192.168.30.13", .IpMachineWise = "192.168.30.226"}
}},
New CompanyConfig With {.Name = "SMART", .Machines = New List(Of MachineConfig) From {
New MachineConfig With {.Name = "M1", .IpMoxa = "192.168.35.10", .IpMachineWise = "192.168.35.212"},
New MachineConfig With {.Name = "M2", .IpMoxa = "192.168.35.11", .IpMachineWise = "192.168.35.211"},
New MachineConfig With {.Name = "M3", .IpMoxa = "192.168.35.12", .IpMachineWise = "192.168.35.213"},
New MachineConfig With {.Name = "M4 - CENTRO LAVORO OKUMA", .IpMoxa = "192.168.35.13", .IpMachineWise = "192.168.35.214"}
}}
}
}
End Function
End Class
Public Class AppConfig
Public Property AdminPasswordHash As String = ""
Public Property AdminPasswordSalt As String = ""
Public Property DefaultCompany As String = ""
Public Property MonitorRefreshSeconds As Integer = 30
Public Property LastReceiveDirectory As String = ""
Public Property DefaultSettings As New TransferSettings()
Public Property Companies As New List(Of CompanyConfig)()
End Class
Public Class CompanyConfig
Public Property Name As String = ""
Public Property Machines As New List(Of MachineConfig)()
End Class
Public Class MachineConfig
Public Property Name As String = ""
Public Property IpMoxa As String = ""
Public Property IpMachineWise As String = ""
Public Property Note As String = ""
End Class
Friend Class MoxaStatusTarget
Public Property Company As String = ""
Public Property Machine As MachineConfig
End Class
Public Class TransferSettings
Public Property Port As Integer = 10001
Public Property ConnectionTimeoutSeconds As Integer = 10
Public Property ReceiveIdleTimeoutSeconds As Integer = 15
Public Property SendTerminator As String = "%"
Public Property LineDelayMs As Integer = 100
Public Property CharDelayMs As Integer = 10
Public Property LinePaced As Boolean = True
Public Property CharPaced As Boolean = False
Public Property BlockPaced As Boolean = False
Public Property BlockSizeBytes As Integer = 32
Public Property BlockDelayMs As Integer = 100
Public Property WaitBeforeSendSeconds As Integer = 2
Public Property WaitAfterSendSeconds As Integer = 2
End Class