laatzen/Pruef2000/source/modProdave6.bas
2021-10-01 11:11:04 +02:00

542 lines
22 KiB
QBasic
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Attribute VB_Name = "modProdave6"
Option Explicit
' PRODAVE S7 declarations in Visual Basic
' for S7_300 or S7_200
'
' Projekt/project -> Eigenschaften/Propertys -> erstellen/build
' S7_300 = 1 or S7_200 = 1
'
'
'
'
'****************************************************************************************************************************
'declarations for S7-200/300/400
'****************************************************************************************************************************
Type tm
tm_hour As Long 'Hours since midnight (0 23)
tm_isdst As Long 'Positive if daylight saving time is in effect; 0 if daylight saving time is not in effect; negative if status of daylight saving time is unknown. The C run-time library assumes the United Statess rules for implementing the calculation of Daylight Saving Time (DST).
tm_mday As Long 'Day of month (1 31)
tm_min As Long 'Minutes after hour (0 59)
tm_mon As Long 'Month (0 11; January = 0)
tm_sec As Long 'Seconds after minute (0 59)
tm_wday As Long 'Day of week (0 6; Sunday = 0)
tm_yday As Long 'Day of year (0 365; January 1 = 0)
tm_year As Long 'Year (current year minus 1900)
End Type
Type TTIMESTAMP
Timestamp(6) As Byte
End Type
Type CON_ADR_TYPE
' MPI Stationsadresse (2 |0 |0 |0 |0 |0 )
' IP Adresse (192|168|0 |1 |0 |0 )
' MAC Adresse (08 |00 |06 |01 |AA |BB )
Adresse(5) As Byte
End Type
Type CON_TABLE_TYPE
Adr As CON_ADR_TYPE ' Verbindungsadresse
AdrType As Byte ' Typ der Adresse MPI(1) IP(2) MAC(3)
SlotNr As Byte ' Slot-Nummer
RackNr As Byte ' Rack-Nummer
End Type
Type AS_INFO_TYPE
Plcas As Long ' TypAusgabestand PLC
Pgas As Integer ' TypAusgabestand PGAS
mlfb As String * 20 ' MLFB der angeschlossenen AS
End Type
Type AS200_INFO_TYPE
Firmware As Long
ASIC As Long
mlfb As String * 16 ' MLFB der angeschlossenen AS
End Type
Type BST_DIAG_TYPE
blknr As Integer ' Bausteinnummer
Timestamp As tm ' Zeitstempel zu der Bausteinnummer
End Type
Type DIAG_BUFFER_TYPE
EventID As Integer ' EreignisID
EventInfo(9) As Byte ' Info zum Ereignis
Timestamp(7) As Byte ' Zeitstempel des Ereignises
End Type
Type BST_STAT_TYPE
blkType As Integer ' Bausteintyp
BlkNumber As Integer ' Bausteinnummer
BlkName As String * 8 ' Bausteinname
BlkVersion As Integer ' Bausteinversion
BlkLength As Long ' Bausteinlänge
BlkTimestamp1 As tm ' Zeitstempel zu der Bausteinnummer
BlkTimestamp2 As tm ' Zeitstempel zu der Bausteinnummer
blkSecurity(3) As Byte ' Baustein Schutzstufe
End Type
Type BST_HEADER_TYPE
ProgLang As Byte ' Programiersprache
blkType As Byte ' Bausteintyp
BlkNumber As Integer ' Bausteinnummer
Attribute As Byte ' Attribute
BlkVersion As Integer ' Bausteinversion
BlkLength As Long ' Bausteinlänge
Length(3) As Long ' Datenlänge
DynLen As Integer ' Länge der dyn. lokal Daten
BlkTimestamp1 As tm ' Zeitstempel zu der Bausteinnummer
BlkTimestamp2 As tm ' Zeitstempel zu der Bausteinnummer
blkSecurity(3) As Byte ' Baustein Schutzstufe
ProducerName As String * 8 ' Producer Name
BlkFamName As String * 8 ' Baustein Familien Name
BlkName As String * 8 ' Bausteinname
Version As Integer ' Dies ist eine Anwenderversion und hat nicht mit der Bausteinversion zu tun
chckSum As Integer ' Checksumme
CPUType As Long ' CPU-Typ
signature As Integer 'signature
End Type
Public Enum EnumProdaveDATATYPE
TypeByte = 1
TypeFloat = 2
End Enum
'****************************************************************************************************************************
'variables for demoprogram
'****************************************************************************************************************************
Global ErrorText As String
Const MAX_BUFFER = 65536
Global ret As Long
Global Amount As Long
Global BLOCKNO As Long
Global no As Long
Global UserID(17) As Byte
Global MPIAdr As Byte
Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
Private Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long
'****************************************************************************************************************************
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpTo As Any, lpFrom As Any, ByVal lLen As Long)
'****************************************************************************************************************************
'declarations for S7-300/400
'****************************************************************************************************************************
Declare Function GetModuleFileNameA Lib "kernel32.dll" (ByVal hModule As Long, szPath As Byte, ByVal szPathLen As Long) As Long
Declare Function LoadConnection_ex6 Lib "Prodave6.dll" _
(ByVal ConNr As Integer, ByVal AccessPoint As String, _
ByVal ConTableLen As Integer, pConTable As CON_TABLE_TYPE) As Long
Declare Function SetActiveConnection_ex6 Lib "Prodave6.dll" (ByVal ConNr As Integer) As Long
Declare Function UnloadConnection_ex6 Lib "Prodave6.dll" (ByVal ConNr As Integer) As Long
Declare Function as_info_ex6 Lib "Prodave6.dll" (ByVal BufLen As Long, pInfoBuffer As AS_INFO_TYPE, _
pDatLen As Long) As Long
Declare Function as_zustand_ex6 Lib "Prodave6.dll" (pState As Byte) As Long
Declare Function db_buch_ex6 Lib "Prodave6.dll" (ByVal BufLen As Long, pBuchBuffer As Byte, pDatLen As Long) As Long
Declare Function db_read_ex6 Lib "Prodave6.dll" (ByVal blknr As Integer, ByVal DatType As Byte, _
ByVal StartNr As Integer, Amount As Long, BufLen As Long, _
pBuchBuffer As Byte, pDatLen As Long) As Long
Declare Function db_write_ex6 Lib "Prodave6.dll" (ByVal blknr As Integer, ByVal DatType As Byte, _
ByVal StartNr As Integer, Amount As Long, BufLen As Long, _
pWriteBuffer As Byte) As Long
Declare Function bst_read_diag_ex6 Lib "Prodave6.dll" (ByVal blkType As Long, ByVal StartNr As Integer, _
Amount As Long, BufLen As Long, _
pDiagBuffer As Byte, pDatLen As Long) As Long
Declare Function bst_read_stat_ex6 Lib "Prodave6.dll" (ByVal blkType As Long, ByVal BufLen As Long, _
pStatBuffer As Byte, ByRef pDatLen As Long) As Long
Declare Function bst_read_ex6 Lib "Prodave6.dll" (ByVal blkType As Integer, ByVal blknr As Integer, _
ByVal BufLen As Long, pReadBuffer As BST_HEADER_TYPE, _
pDatLen As Long) As Long
Declare Function read_diag_buf_ex6 Lib "Prodave6.dll" (ByVal BufLen As Long, pDiagBuffer As Byte, _
pDatLen As Long) As Long
Declare Function field_read_ex6 Lib "Prodave6.dll" (ByVal Fieldtype As Byte, ByVal blknr As Integer, _
ByVal StartNr As Integer, ByVal Amount As Long, _
BufLen As Long, pBuffer As Byte, pDatLen As Long) As Long
Declare Function field_write_ex6 Lib "Prodave6.dll" (ByVal Fieldtype As Byte, ByVal blknr As Integer, _
ByVal StartNr As Integer, ByVal Amount As Long, _
BufLen As Long, pBuffer As Byte) As Long
Declare Function mb_setbit_ex6 Lib "Prodave6.dll" (ByVal MbNr As Integer, ByVal bitNr As Byte, _
ByVal value As Byte) As Long
Declare Function mb_bittest_ex6 Lib "Prodave6.dll" (ByVal MbNr As Integer, ByVal bitNr As Byte, _
value As Byte) As Long
'****************************************************************************************************************************
'AS200 Funktionen
'****************************************************************************************************************************
Declare Function as200_as_info_ex6 Lib "Prodave6.dll" (ByVal BufLen As Long, pBuffer As AS200_INFO_TYPE, _
pDatLen As Long) As Long
Declare Function as200_as_zustand_ex6 Lib "Prodave6.dll" (pState As Byte) As Long
Declare Function as200_field_read_ex6 Lib "Prodave6.dll" (ByVal Fieldtype As Byte, ByVal blknr As Integer, _
ByVal StartNr As Integer, Amount As Long, BufLen As Long, _
pBuffer As Byte, pDatLen As Long) As Long
Declare Function as200_field_write_ex6 Lib "Prodave6.dll" (ByVal Fieldtype As Byte, ByVal blknr As Integer, _
ByVal StartNr As Integer, Amount As Long, BufLen As Long, _
pBuffer As Byte) As Long
Declare Function as200_mb_setbit_ex6 Lib "Prodave6.dll" (ByVal MbNr As Integer, ByVal bitNr As Byte, _
ByVal value As Byte) As Long
Declare Function as200_mb_bittest_ex6 Lib "Prodave6.dll" (ByVal MbNr As Integer, ByVal bitNr As Byte, _
value As Byte) As Long
'****************************************************************************************************************************
'Komfort Funktionen
'****************************************************************************************************************************
Declare Function GetErrorMessage_ex6 Lib "Prodave6.dll" (ByVal ErrorNr As Long, ByVal BufLen As Long, _
pBuffer As Byte) As Long
Declare Function kg_2_float_ex6 Lib "Prodave6.dll" (ByVal kg As Long, pieee As Double) As Long
Declare Function float_2_kg_ex6 Lib "Prodave6.dll" (ieee As Double, pkg As Long) As Long
Declare Function gp_2_float_ex6 Lib "Prodave6.dll" (ByVal gp As Long, pieee As Double) As Long
Declare Function float_2_gp_ex6 Lib "Prodave6.dll" (ieee As Double, pgp As Long) As Long
Declare Function testbit_ex6 Lib "Prodave6.dll" (value As Byte, bitNr As Byte) As Integer
Declare Function byte_2_bool_ex6 Lib "Prodave6.dll" (ByVal value As Long, pBuffer As Integer)
Declare Function bool_2_byte_ex6 Lib "Prodave6.dll" (pBuffer As Integer) As Byte
Declare Function kf_2_integer_ex6 Lib "Prodave6.dll" (ByVal wValue As Integer) As Integer
Declare Function kf_2_long_ex6 Lib "Prodave6.dll" (ByVal dwValue As Long) As Long
Declare Function swab_buffer_ex6 Lib "Prodave6.dll" (pBuffer As Byte, Amount As Long)
Declare Function copy_buffer_ex6 Lib "Prodave6.dll" (pTargetBuffer As Byte, pSourceBuffer As Byte, Amount As Long)
Declare Function ushort_2_bcd_ex6 Lib "Prodave6.dll" (pwValues As Integer, ByVal Amount As Long, _
ByVal InBytechange As Integer, ByVal OutBytechange As Integer)
Declare Function ulong_2_bcd_ex6 Lib "Prodave6.dll" (pdwValues As Long, ByVal Amount As Long, _
ByVal InBytechange As Integer, ByVal OutBytechange As Integer)
Declare Function bcd_2_ushort_ex6 Lib "Prodave6.dll" (pwValues As Integer, ByVal Amount As Long, _
ByVal InBytechange As Integer, ByVal OutBytechange As Integer)
Declare Function bcd_2_ulong_ex6 Lib "Prodave6.dll" (pdwValues As Long, ByVal Amount As Long, _
ByVal InBytechange As Integer, ByVal OutBytechange As Integer)
Declare Function GetLoadedConnections_ex6 Lib "Prodave6.dll" (ByVal BufLen As Long, pBuffer As Integer)
'****************************************************************************************************************************
'Funktions
'****************************************************************************************************************************
'Wandelt ein ByteArray in einen String um
Public Function ByteToString(Target As String, byteArray() As Byte, ByVal Amount As Long) As Boolean
Dim i As Integer
Dim MyChar As String
For i = 0 To Amount
' neu RH 21.5.2012: String ist durch Null-Byte terminiert
If byteArray(i) = 0 Then
Exit For
End If
MyChar = MyChar & Chr(byteArray(i))
Next
Target = MyChar
End Function
'Wandelt einen HexString in eine Dezimalzahl um
Public Function HexToDec(strHex As String) As Long
Select Case (Right(strHex, 1))
Case "0"
HexToDec = 0 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "1"
HexToDec = 1 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "2"
HexToDec = 2 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "3"
HexToDec = 3 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "4"
HexToDec = 4 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "5"
HexToDec = 5 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "6"
HexToDec = 6 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "7"
HexToDec = 7 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "8"
HexToDec = 8 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "9"
HexToDec = 9 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "a", "A"
HexToDec = 10 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "b", "B"
HexToDec = 11 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "c", "C"
HexToDec = 12 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "d", "D"
HexToDec = 13 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "e", "E"
HexToDec = 14 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case "f", "F"
HexToDec = 15 + (16 * (HexToDec(Left(strHex, Len(strHex) - 1))))
Case Else
HexToDec = 0
End Select
End Function
Public Function HexToDec2(HexValue As String) As Long
On Error Resume Next
If UCase$(Left$(HexValue, 2)) = "0X" Then
HexToDec2 = Val("&H" & Mid$(HexValue, 3))
ElseIf UCase$(Left$(HexValue, 2)) <> "&H" Then
HexToDec2 = Val("&H" & HexValue)
Else
HexToDec2 = Val(HexValue)
End If
End Function
' Reinhard Henning 2012
' Wandelt einen Single Wert in einen 4 Byte-Gleitkommawert nach IEEE um
Public Sub SngToBuffer(sngInput As Single, Startbyte As Integer, Buffer() As Byte)
Dim temp(0 To 3) As Byte
CopyMemory temp(0), sngInput, 4
Buffer(Startbyte + 3) = temp(0)
Buffer(Startbyte + 2) = temp(1)
Buffer(Startbyte + 1) = temp(2)
Buffer(Startbyte + 0) = temp(3)
End Sub
' Reinhard Henning 2012
' Wandelt eine 4 Byte-Gleitkommawert nach IEEE in einen Single Wert um
Public Sub BufferToSng(Buffer() As Byte, Startbyte As Integer, Dest As Single)
Dim temp(0 To 3) As Byte
temp(0) = Buffer(Startbyte + 3)
temp(1) = Buffer(Startbyte + 2)
temp(2) = Buffer(Startbyte + 1)
temp(3) = Buffer(Startbyte)
CopyMemory Dest, temp(0), 4
End Sub
Public Function connect(Station As Byte, Rack As Byte, Slot As Byte) As Boolean
On Error GoTo Errorhandler
Dim pConTable As CON_TABLE_TYPE
Dim ConTableLen As Integer
Dim AccessPoint As String
Dim MyHex As String
Dim ConNr As Integer
Dim ret As Long
Dim lngRet As Long
pConTable.AdrType = 1 'MPI = 1 IP = 2 MAC = 3
pConTable.RackNr = Rack
pConTable.SlotNr = Slot
pConTable.Adr.Adresse(0) = Station
pConTable.Adr.Adresse(1) = 0
pConTable.Adr.Adresse(2) = 0
pConTable.Adr.Adresse(3) = 0
pConTable.Adr.Adresse(4) = 0
pConTable.Adr.Adresse(5) = 0
ConTableLen = 9
ConNr = 0
AccessPoint = "S7ONLINE"
MyHex = LoadConnection_ex6(ConNr, AccessPoint, ConTableLen, pConTable)
ret = MyHex
If ret = 0 Then
connect = True
Else
ProdaveFehlerAnzeigen ret
End If
Exit Function
Errorhandler:
If Err.Number = 53 Then
MsgBox "Fehler " & Err.Number & " in modProdave6.Connect(). 'LoadConnection_ex6' ist fehlgeschlagen. " & Err.Description & vbCrLf & "Prodave scheint nicht richtig installiert zu sein."
Else
MsgBox "Fehler " & Err.Number & " in modProdave6.Connect(). 'LoadConnection_ex6' ist fehlgeschlagen. " & Err.Description
End If
End Function
Public Sub ProdaveFehlerAnzeigen(lngRet As Long)
Dim errorBuffer(256) As Byte
Dim MyChar As String
Dim strHex
strHex = Hex(lngRet)
ret = GetErrorMessage_ex6(lngRet, 256, errorBuffer(0))
Call ByteToString(MyChar, errorBuffer, 200)
If MyChar <> "" Then
Call MsgBox(MyChar, vbOKOnly, "Procave LoadConnection_ex6 Fehler 0x" & strHex)
Else
MsgBox "Prodave (LoadConnection_ex6) Fehler 0x" & strHex & " (Fehlertext kann nicht angezeigt werden weil Prodave datei error.dat fehlt)"
End If
End Sub
Public Function Dicconnect() As Boolean
Dim ConNr As Integer
Dim MyHex As String
Dim ret As Long
ConNr = 0
MyHex = UnloadConnection_ex6(ConNr)
ret = MyHex
If (ret = 0) Or (ret = 28720) Then
Dicconnect = True
Else
ProdaveFehlerAnzeigen ret
End If
Dicconnect = False
End Function
Public Function GetVariablenAdrTypeDb(ByVal strVarname As String, ByRef StartAddess As Integer, ByRef DataType As EnumProdaveDATATYPE, ByRef byteDatenbausteinNr As Byte) As Boolean
Dim lLen As Integer
Dim sBuffer As String
sBuffer = String(256, Chr$(0))
Dim strAdresse As String
Dim strType As String
'lLen = GetPrivateProfileString("Prodave", strVarname, "", sBuffer, Len(sBuffer), "ProdaveMapping.ini")
'sBuffer = Left$(sBuffer, lLen)
sBuffer = g_App.Settings.readStringValue("Prodave-Variabeln", strVarname, "")
If sBuffer = "" Then
WriteToLog "StartAdr und Type Angaben zur Prodave Variablen " & strVarname & " konnten nicht gefunden werden."
LogIntoDB "StartAdr und Type Angaben zur Prodave Variablen " & strVarname & " konnten nicht gefunden werden."
GetVariablenAdrTypeDb = False
Exit Function
Else
strAdresse = Split(sBuffer, " ")(0)
If IsNumeric(strAdresse) Then
StartAddess = CInt(strAdresse)
GetVariablenAdrTypeDb = True
Else
MsgBox "StartAdr " & strAdresse & " zur Prodave Variablen " & strVarname & " ist nicht numerisch"
GetVariablenAdrTypeDb = False
Exit Function
End If
strType = Split(sBuffer, " ")(1)
Select Case UCase(strType)
Case "BYTE"
DataType = TypeByte
Case "FLOAT"
DataType = TypeFloat
Case Else
MsgBox "Type Angabe " & strType & " zur Prodave Variablen " & strVarname & " ist nicht definiert."
GetVariablenAdrTypeDb = False
Exit Function
End Select
Dim strTemp As String
strTemp = Split(sBuffer, " ")(2)
byteDatenbausteinNr = Val(strTemp)
Select Case byteDatenbausteinNr
Case 120, 121, 20, 43, 44
Case Else
MsgBox "Prodave BausteinNr " & byteDatenbausteinNr & " (für " & strVarname & ") ist ungültig!"
End Select
End If
End Function
Public Sub CreateInIni(strVarname As String, strWert As String)
g_App.Settings.saveStringValue "Prodave-Variabeln", strVarname, strWert
End Sub
Public Sub CreateIniWerte()
'CreateInIni "PT_BetrArt", "120 BYTE"
'CreateInIni "PT_Strecke1", "122 BYTE"
'CreateInIni "PT_P1Status", "124 BYTE"
'CreateInIni "PT_P2Status", "125 BYTE"
'CreateInIni "PT_P3Status", "126 BYTE"
'CreateInIni "PT_M1Status", "127 BYTE"
'CreateInIni "PT_M2Status", "128 BYTE"
'CreateInIni "PT_M3Status", "129 BYTE"
'CreateInIni "PT_M4Status", "130 BYTE"
'
'CreateInIni "PT_Stoer0", "200 BYTE"
'CreateInIni "PT_Stoer1", "201 BYTE"
'CreateInIni "PT_Stoer2", "202 BYTE"
'CreateInIni "PT_Stoer3", "203 BYTE"
'CreateInIni "PT_Stoer4", "204 BYTE"
'CreateInIni "PT_Stoer5", "205 BYTE"
'CreateInIni "PT_Stoer6", "206 BYTE"
'CreateInIni "PT_Stoer7", "207 BYTE"
'CreateInIni "PT_Stoer8", "208 BYTE"
'CreateInIni "PT_Stoer9", "209 BYTE"
'CreateInIni "PT_T_Einlauf", "144 BYTE"
'CreateInIni "PT_Qist", "140 FLOAT"
'CreateInIni "PT_Regler", "131 BYTE"
'CreateInIni "PT_PrfStatus", "132 BYTE"
'CreateInIni "PT_Waage", "133 BYTE"
'CreateInIni "PT_Fuell1", "240 FLOAT"
'CreateInIni "PT_Fuell2", "244 FLOAT"
'
'CreateInIni "VB_Betrieb", "160 BYTE"
'CreateInIni "VB_MidGr", "161 BYTE"
'CreateInIni "VB_Ablass", "170 BYTE"
'CreateInIni "VB_MidNr", "162 BYTE"
'CreateInIni "VB_Behaelter", "163 BYTE"
'
'CreateInIni "VB_Pumpe1", "164 BYTE"
'CreateInIni "VB_Pumpe2", "165 BYTE"
'CreateInIni "VB_Pumpe3", "166 BYTE"
'CreateInIni "VB_Pumpe5", "167 BYTE"
'
'CreateInIni "VB_Stoer0", "170 BYTE"
'CreateInIni "VB_Stoer1", "171 BYTE"
'CreateInIni "VB_Stoer2", "172 BYTE"
'CreateInIni "VB_Stoer3", "173 BYTE"
'CreateInIni "VB_Stoer4", "174 BYTE"
'CreateInIni "VB_Stoer5", "175 BYTE"
'CreateInIni "VB_Stoer6", "176 BYTE"
'CreateInIni "VB_Stoer7", "177 BYTE"
'CreateInIni "VB_Stoer8", "178 BYTE"
'CreateInIni "VB_Stoer9", "179 BYTE"
'
'CreateInIni "VB_QSoll", "180 FLOAT"
'CreateInIni "VB_Einsatz", "168 BYTE"
'CreateInIni "VB_RegelArt", "169 BYTE"
'
'CreateInIni "VB_QDiff", "184 FLOAT"
'CreateInIni "VB_Regu", "188 BYTE"
'CreateInIni "VB_ServoSt", "192 BYTE"
End Sub