Modbus ����Ӧ��
���ĵ����� Modbus ���ĸ����ܡ����ʵ���ͳ���Ӧ�ó�����
? Ŀ¼
������
1. ����վ��һģʽ
ijЩӦ�ó�����Ҫͬһ�豸����Ϊ��վ����Ϊ��վ:
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
Private WithEvents mbSlave As cModbusSlave
Private Sub Form_Load()
Set mbMaster = New cModbusMaster
Set mbSlave = New cModbusSlave
' ��ʼ����վ - ����λ����������
mbMaster.ProtocolType = MB_MASTER_PROTOCOL_TCP
mbMaster.TCPHost = "192.168.1.100"
mbMaster.TCPPort = 502
mbMaster.SlaveID = 1
' ��ʼ����վ - ����λ���ṩ����
mbSlave.ProtocolType = MB_SLAVE_PROTOCOL_TCP
mbSlave.SlaveID = 2
mbSlave.BindAddress = "0.0.0.0" ' �������нӿ�
mbSlave.Start 1502
End Sub
' ����վ�������ݺ�,���´�վ�ṩ����λ��
Private Sub tmrSync_Timer()
On Error Resume Next
' ����λ����ȡ
Dim iRegs() As Integer
iRegs = mbMaster.ReadHoldingRegisters(0, 10)
' д�뱾�ش�վ,����λ����ȡ
If UBound(iRegs) >= 0 Then
Dim i As Long
For i = 0 To UBound(iRegs)
mbSlave.SetHoldingRegister i, iRegs(i)
Next i
End If
End Sub2. �������
ʵ�ִ����Ե��������:
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
Private Const MAX_RETRIES As Long = 3
Private Const RETRY_DELAY_MS As Long = 1000
Private Function ReadWithRetry(StartAddr As Long, Quantity As Long) As Integer()
Dim iRetry As Long
Dim bSuccess As Boolean
Dim iRegs() As Integer
For iRetry = 1 To MAX_RETRIES
On Error Resume Next
If mbMaster.State = MB_MASTER_STATE_DISCONNECTED Then
mbMaster.Connect
End If
iRegs = mbMaster.ReadHoldingRegisters(StartAddr, Quantity)
If UBound(iRegs) >= 0 Then
bSuccess = True
Exit For
End If
' ʧ�ܺ�ȴ�
If iRetry < MAX_RETRIES Then
Sleep RETRY_DELAY_MS
End If
Next iRetry
If bSuccess Then
ReadWithRetry = iRegs
Else
' �������Զ�ʧ��
RaiseError "ReadWithRetry failed after " & MAX_RETRIES & " attempts"
End If
End Function3. �첽����ģʽ
ʹ�� Timer ʵ�ַ��������첽����:
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
Private WithEvents tmrAsync As Timer
Private m_lPendingAddr As Long
Private m_lPendingCount As Long
Private m_bAsyncBusy As Boolean
Private Sub Form_Load()
Set mbMaster = New cModbusMaster
Set tmrAsync = New Timer
tmrAsync.Interval = 100 ' 100ms �����
End Sub
' �����첽��ȡ����
Public Sub AsyncReadHoldingRegisters(StartAddr As Long, Quantity As Long)
If m_bAsyncBusy Then
Debug.Print "Async operation in progress"
Exit Sub
End If
m_bAsyncBusy = True
m_lPendingAddr = StartAddr
m_lPendingCount = Quantity
tmrAsync.Enabled = True
End Sub
' Timer �ص�ִ�ж�ȡ
Private Sub tmrAsync_Timer()
On Error Resume Next
tmrAsync.Enabled = False
Dim iRegs() As Integer
iRegs = mbMaster.ReadHoldingRegisters(m_lPendingAddr, m_lPendingCount)
If UBound(iRegs) >= 0 Then
' ��ȡ�ɹ�,�����ص�
OnAsyncReadComplete m_lPendingAddr, m_lPendingCount, iRegs
Else
' ��ȡʧ��
OnAsyncError "Read failed"
End If
m_bAsyncBusy = False
End Sub
' ��ȡ��ɻص�
Private Sub OnAsyncReadComplete(StartAddr As Long, Quantity As Long, ByRef Values() As Integer)
' ��������
ProcessData Values
RaiseEvent AsyncReadComplete(StartAddr, Quantity, Values)
End Sub
' ����ص�
Private Sub OnAsyncError(Description As String)
Debug.Print "Async error: " & Description
RaiseEvent AsyncError(Description)
End Sub������
1. ������ȡ�Ż�
����ʹ�õ���������ȡ,����ͨ�Ŵ���:
vb
' ������� - ��ζ�ȡ
Private Sub BadReadApproach()
Dim i As Long
Dim iValue As Integer
For i = 0 To 99
iValue = mbMaster.ReadHoldingRegisters(i, 1)(0)
' ��������
Next i
End Sub
' �õ����� - ������ȡ
Private Sub GoodReadApproach()
Dim iRegs() As Integer
Dim i As Long
' һ�ζ�ȡ100���Ĵ���
iRegs = mbMaster.ReadHoldingRegisters(0, 100)
For i = 0 To UBound(iRegs)
' ��������
Next i
End Sub2. ��ȡƵ�ʿ���
�������Ƶ���Ķ�ȡ����:
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
Private WithEvents tmrPoll As Timer
Private m_dLastReadTime As Double
Private Const MIN_READ_INTERVAL_SEC As Double = 0.5 ' �����0.5��
Private Sub tmrPoll_Timer()
Dim dNow As Double
dNow = Timer
' ����Ƿ���С���
If dNow - m_dLastReadTime < MIN_READ_INTERVAL_SEC Then
Exit Sub
End If
' ִ�ж�ȡ
Dim iRegs() As Integer
iRegs = mbMaster.ReadHoldingRegisters(0, 10)
m_dLastReadTime = dNow
End Sub3. ����Ԥ����
����ʱԤ���س�������:
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
Private m_iRegCache() As Integer
Private Const CACHE_SIZE As Long = 100
Private Sub mbMaster_OnConnect()
' ���ӳɹ����������ػ���
LoadCache
End Sub
Private Sub LoadCache()
On Error Resume Next
ReDim m_iRegCache(CACHE_SIZE - 1) As Integer
Dim iRegs() As Integer
iRegs = mbMaster.ReadHoldingRegisters(0, CACHE_SIZE)
If UBound(iRegs) >= 0 Then
Dim i As Long
For i = 0 To UBound(iRegs)
If i < CACHE_SIZE Then
m_iRegCache(i) = iRegs(i)
End If
Next i
Debug.Print "Cache loaded: " & (UBound(iRegs) + 1) & " registers"
End If
End Sub
' �ӻ�����ٶ�ȡ
Public Function GetCachedRegister(Addr As Long) As Integer
If Addr >= 0 And Addr < CACHE_SIZE Then
GetCachedRegister = m_iRegCache(Addr)
Else
GetCachedRegister = -1 ' �������
End If
End Function
' ���ڸ��»���
Private Sub tmrCacheUpdate_Timer()
On Error Resume Next
Dim iRegs() As Integer
iRegs = mbMaster.ReadHoldingRegisters(0, CACHE_SIZE)
If UBound(iRegs) >= 0 Then
Dim i As Long
For i = 0 To UBound(iRegs)
If i < CACHE_SIZE Then
m_iRegCache(i) = iRegs(i)
End If
Next i
End If
End Sub4. ���ӳع���
���ڶ��վ����,ʵ�����ӳ�:
vb
Option Explicit
Private Type ModbusDevice
ID As Long
Host As String
Port As Long
SlaveID As Byte
LastUsed As Double
Connected As Boolean
End Type
Private m_Devices() As ModbusDevice
Private m_MaxDevices As Long
Private m_ConnectionTimeout As Double ' ��
Private Sub InitializeConnectionPool(MaxDevices As Long, TimeoutSeconds As Double)
m_MaxDevices = MaxDevices
m_ConnectionTimeout = TimeoutSeconds
ReDim m_Devices(MaxDevices - 1) As ModbusDevice
Dim i As Long
For i = 0 To MaxDevices - 1
m_Devices(i).Connected = False
m_Devices(i).LastUsed = Timer - m_ConnectionTimeout - 1
Next i
End Sub
' ��ȡ�豸����
Public Function GetDevice(DeviceID As Long) As cModbusMaster
Dim mbDev As cModbusMaster
Dim i As Long
' �����豸
For i = 0 To m_MaxDevices - 1
If m_Devices(i).ID = DeviceID Then
' ����ʹ��ʱ��
m_Devices(i).LastUsed = Timer
Set mbDev = New cModbusMaster
mbDev.ProtocolType = MB_MASTER_PROTOCOL_TCP
mbDev.TCPHost = m_Devices(i).Host
mbDev.TCPPort = m_Devices(i).Port
mbDev.SlaveID = m_Devices(i).SlaveID
Set GetDevice = mbDev
Exit Function
End If
Next i
Set GetDevice = Nothing
End Function
' ������������
Public Sub CleanupConnections()
Dim i As Long
Dim dNow As Double
dNow = Timer
For i = 0 To m_MaxDevices - 1
If m_Devices(i).Connected Then
' ������ʱʱ��
If dNow - m_Devices(i).LastUsed > m_ConnectionTimeout Then
' ���Ϊ�Ͽ�
m_Devices(i).Connected = False
Debug.Print "Device " & m_Devices(i).ID & " connection timed out"
End If
End If
Next i
End Sub������
1. �ۺϴ�����
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
' ��ȡ����������
Public Function SafeReadRegisters(StartAddr As Long, Quantity As Long) As Integer()
On Error GoTo ErrorHandler
' �������״̬
If mbMaster.State <> MB_MASTER_STATE_CONNECTED Then
Err.Raise vbObjectError + 1, "SafeReadRegisters", "Not connected"
End If
' ������
If StartAddr < 0 Then
Err.Raise vbObjectError + 2, "SafeReadRegisters", "Invalid start address"
End If
If Quantity < 1 Or Quantity > mbMaster.Defaults.MAX_REGISTERS Then
Err.Raise vbObjectError + 3, "SafeReadRegisters", "Invalid quantity"
End If
' ִ�ж�ȡ
Dim iRegs() As Integer
iRegs = mbMaster.ReadHoldingRegisters(StartAddr, Quantity)
SafeReadRegisters = iRegs
Exit Function
ErrorHandler:
Dim sError As String
sError = "Error " & Err.Number & ": " & Err.Description
Debug.Print sError
LogError sError
' ���ؿ�����
SafeReadRegisters = Array()
End Function
' ������־
Private Sub LogError(sMessage As String)
Dim iFile As Integer
iFile = FreeFile
Open App.Path & "\modbus_error.log" For Append As #iFile
Print #iFile, Format$(Now, "yyyy-mm-dd hh:mm:ss") & " - " & sMessage
Close #iFile
End Sub2. �쳣�봦��
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
Private Sub mbMaster_OnError(ByVal Description As String)
Select Case True
Case InStr(Description, "Illegal Function") > 0
HandleIllegalFunction
Case InStr(Description, "Illegal Data Address") > 0
HandleIllegalDataAddress
Case InStr(Description, "Illegal Data Value") > 0
HandleIllegalDataValue
Case InStr(Description, "Slave Device Failure") > 0
HandleSlaveDeviceFailure
Case InStr(Description, "Slave Device Busy") > 0
HandleSlaveDeviceBusy
Case InStr(Description, "Response timeout") > 0
HandleTimeout
Case Else
HandleGenericError Description
End Select
End Sub
Private Sub HandleIllegalFunction()
Debug.Print "�����벻֧��"
' ������Ҫʹ�ò�ͬ�Ĺ�����
End Sub
Private Sub HandleIllegalDataAddress()
Debug.Print "��ַ������Χ"
' ����ַ�Ƿ�����Ч��Χ��
End Sub
Private Sub HandleIllegalDataValue()
Debug.Print "����ֵ�Ƿ�"
' ���д�������ֵ
End Sub
Private Sub HandleSlaveDeviceFailure()
Debug.Print "��վ�豸����"
' ������Ҫ�������л��������豸
End Sub
Private Sub HandleSlaveDeviceBusy()
Debug.Print "��վæ"
' �ȴ�������
Sleep 1000
End Sub
Private Sub HandleTimeout()
Debug.Print "��Ӧ��ʱ"
' ������������
' ������Ҫ����
End Sub
Private Sub HandleGenericError(sDescription As String)
Debug.Print "ͨ�ô���: " & sDescription
End Sub���վ����
1. �豸���ù���
vb
Option Explicit
Private Type DeviceConfig
DeviceID As Long
Name As String
ProtocolType As ModbusMasterProtocolType
Host As String
Port As Long
SerialPort As String
BaudRate As Long
SlaveID As Byte
PollInterval As Long ' ��ѯ���(����)
Enabled As Boolean
End Type
Private m_Devices() As DeviceConfig
Private m_MasterConnections() As cModbusMaster
' �����豸����
Public Sub AddDevice(DevID As Long, sName As String, Protocol As ModbusProtocolType, _
sHost As String, lPort As Long, sSerialPort As String, _
lBaudRate As Long, bSlaveID As Byte, lPollInterval As Long)
Dim iCount As Long
iCount = UBound(m_Devices) + 1
ReDim Preserve m_Devices(iCount) As DeviceConfig
ReDim Preserve m_MasterConnections(iCount) As cModbusMaster
With m_Devices(iCount)
.DeviceID = DevID
.Name = sName
.ProtocolType = Protocol
.Host = sHost
.Port = lPort
.SerialPort = sSerialPort
.BaudRate = lBaudRate
.SlaveID = bSlaveID
.PollInterval = lPollInterval
.Enabled = True
End With
Set m_MasterConnections(iCount) = New cModbusMaster
Debug.Print "Device added: " & sName & " (ID: " & DevID & ")"
End Sub
' ���������豸
Public Sub ConnectAllDevices()
Dim i As Long
For i = 0 To UBound(m_Devices)
If m_Devices(i).Enabled Then
ConnectDevice i
End If
Next i
End Sub
' ���ӵ����豸
Private Sub ConnectDevice(Index As Long)
On Error Resume Next
Dim mbDev As cModbusMaster
Set mbDev = m_MasterConnections(Index)
With m_Devices(Index)
mbDev.ProtocolType = .ProtocolType
mbDev.SlaveID = .SlaveID
If .ProtocolType = MB_MASTER_PROTOCOL_TCP Then
mbDev.TCPHost = .Host
mbDev.TCPPort = .Port
mbDev.Connect
Else
mbDev.SerialPort = .SerialPort
mbDev.BaudRate = .BaudRate
mbDev.DataBits = 8
mbDev.Parity = "N"
mbDev.StopBits = 1
mbDev.Connect .SerialPort
End If
End With
If Err.Number = 0 Then
Debug.Print "Device " & m_Devices(Index).Name & " connected"
Else
Debug.Print "Device " & m_Devices(Index).Name & " connect failed: " & Err.Description
End If
End Sub2. ͳһ��ѯ����
vb
Option Explicit
Private WithEvents tmrPoll As Timer
Private m_CurrentDeviceIndex As Long
Private Sub StartPolling()
Set tmrPoll = New Timer
tmrPoll.Interval = 100 ' 100ms ��ѯ���
m_CurrentDeviceIndex = 0
tmrPoll.Enabled = True
End Sub
Private Sub tmrPoll_Timer()
tmrPoll.Enabled = False
' ��ѯ��ǰ�豸
PollDevice m_CurrentDeviceIndex
' �ƶ�����һ���豸
m_CurrentDeviceIndex = m_CurrentDeviceIndex + 1
If m_CurrentDeviceIndex > UBound(m_Devices) Then
m_CurrentDeviceIndex = 0
End If
tmrPoll.Enabled = True
End Sub
Private Sub PollDevice(Index As Long)
On Error Resume Next
Dim mbDev As cModbusMaster
Set mbDev = m_MasterConnections(Index)
' ����豸�Ƿ�����
If Not m_Devices(Index).Enabled Then
Exit Sub
End If
' �������״̬
If mbDev.State <> MB_MASTER_STATE_CONNECTED Then
ConnectDevice Index
Exit Sub
End If
' ��ȡ�豸����
Dim iRegs() As Integer
iRegs = mbDev.ReadHoldingRegisters(0, 10)
If UBound(iRegs) >= 0 Then
' ��������
ProcessDeviceData m_Devices(Index).DeviceID, iRegs
' �������ͨ��ʱ��
m_Devices(Index).LastUsed = Timer
End If
End Sub
Private Sub ProcessDeviceData(DeviceID As Long, ByRef Values() As Integer)
' �����豸ID��������
Select Case DeviceID
Case 1
ProcessDevice1Data Values
Case 2
ProcessDevice2Data Values
' ...
End Select
End Sub���ݻ������
1. �༶����
vb
Option Explicit
Private Enum CacheLevel
CACHE_LEVEL_NONE = 0
CACHE_LEVEL_L1 = 1 ' �ڴ滺��
CACHE_LEVEL_L2 = 2 ' �����
CACHE_LEVEL_L3 = 3 ' ���ݿ��
End Enum
Private m_L1Cache() As Integer ' �ڴ滺��
Private m_L1CacheValid() As Boolean ' ������Ч�Ա��
Private m_CacheLevel As CacheLevel
' ��ʼ������
Public Sub InitializeCache(Size As Long, Level As CacheLevel)
ReDim m_L1Cache(Size - 1) As Integer
ReDim m_L1CacheValid(Size - 1) As Boolean
m_CacheLevel = Level
Dim i As Long
For i = 0 To Size - 1
m_L1CacheValid(i) = False
Next i
End Sub
' ��ȡ�Ĵ���(������)
Public Function ReadWithCache(Addr As Long) As Integer
' �ȼ��L1����
If Addr >= 0 And Addr <= UBound(m_L1Cache) Then
If m_L1CacheValid(Addr) And m_CacheLevel >= CACHE_LEVEL_L1 Then
ReadWithCache = m_L1Cache(Addr)
Debug.Print "Cache L1 hit: Reg[" & Addr & "] = " & m_L1Cache(Addr)
Exit Function
End If
End If
' L1����δ����,���豸��ȡ
Dim iRegs() As Integer
iRegs = mbMaster.ReadHoldingRegisters(Addr, 1)
If UBound(iRegs) >= 0 Then
' ����L1����
If Addr >= 0 And Addr <= UBound(m_L1Cache) Then
m_L1Cache(Addr) = iRegs(0)
m_L1CacheValid(Addr) = True
End If
ReadWithCache = iRegs(0)
Debug.Print "Cache miss: Reg[" & Addr & "] = " & iRegs(0)
End If
End Function
' ��������
Public Sub InvalidateCache(Addr As Long)
On Error Resume Next
If Addr >= 0 And Addr <= UBound(m_L1Cache) Then
m_L1CacheValid(Addr) = False
End If
End Sub
' ���������
Public Sub InvalidateAllCache()
Dim i As Long
For i = 0 To UBound(m_L1Cache)
m_L1CacheValid(i) = False
Next i
Debug.Print "All cache invalidated"
End Sub2. д��ͬ������
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
Private WithEvents mbSlave As cModbusSlave
' ��վ��ȡ��ͬ������վ
Private Sub SyncMasterToSlave()
On Error Resume Next
' ����λ����ȡ
Dim iMasterRegs() As Integer
iMasterRegs = mbMaster.ReadHoldingRegisters(0, 10)
If UBound(iMasterRegs) >= 0 Then
' �Ƚ������Ƿ�仯
Dim bChanged As Boolean
bChanged = CheckDataChanged(iMasterRegs)
' ����б仯,ͬ������վ
If bChanged Then
Dim i As Long
For i = 0 To UBound(iMasterRegs)
mbSlave.SetHoldingRegister i, iMasterRegs(i)
Next i
Debug.Print "Synced " & (UBound(iMasterRegs) + 1) & " registers"
End If
End If
End Sub
' ��������Ƿ�仯
Private Function CheckDataChanged(ByRef NewData() As Integer) As Boolean
Static iLastData() As Integer
Static bInitialized As Boolean
If Not bInitialized Then
' �״γ�ʼ��
iLastData = NewData
bInitialized = True
CheckDataChanged = True
Exit Function
End If
Dim i As Long
For i = 0 To UBound(NewData)
If iLastData(i) <> NewData(i) Then
iLastData(i) = NewData(i)
CheckDataChanged = True
Exit Function
End If
Next i
CheckDataChanged = False
End Function��־�����
1. ��ϸ��־��¼
vb
Option Explicit
Private Enum LogLevel
LOG_LEVEL_DEBUG = 0
LOG_LEVEL_INFO = 1
LOG_LEVEL_WARNING = 2
LOG_LEVEL_ERROR = 3
LOG_LEVEL_CRITICAL = 4
End Enum
Private m_LogLevel As LogLevel
Private m_LogFile As String
' ��ʼ����־
Public Sub InitLog(sFilePath As String, Level As LogLevel)
m_LogFile = sFilePath
m_LogLevel = Level
' ������־Ŀ¼
Dim sDir As String
sDir = Left$(sFilePath, InStrRev(sFilePath, "\") - 1)
On Error Resume Next
MkDir sDir
On Error GoTo 0
End Sub
' д����־
Public Sub WriteLog(Level As LogLevel, sMessage As String)
' ֻ��¼ָ���������ϵ���־
If Level < m_LogLevel Then Exit Sub
Dim sLevel As String
Select Case Level
Case LOG_LEVEL_DEBUG: sLevel = "DEBUG"
Case LOG_LEVEL_INFO: sLevel = "INFO "
Case LOG_LEVEL_WARNING: sLevel = "WARN "
Case LOG_LEVEL_ERROR: sLevel = "ERROR"
Case LOG_LEVEL_CRITICAL: sLevel = "CRIT "
End Select
Dim sLogLine As String
sLogLine = Format$(Now, "yyyy-mm-dd hh:mm:ss") & " [" & sLevel & "] " & sMessage
Debug.Print sLogLine
On Error Resume Next
Dim iFile As Integer
iFile = FreeFile
Open m_LogFile For Append As #iFile
Print #iFile, sLogLine
Close #iFile
On Error GoTo 0
End Sub
' ��װ����־����
Public Sub LogDebug(sMessage As String)
WriteLog LOG_LEVEL_DEBUG, sMessage
End Sub
Public Sub LogInfo(sMessage As String)
WriteLog LOG_LEVEL_INFO, sMessage
End Sub
Public Sub LogWarning(sMessage As String)
WriteLog LOG_LEVEL_WARNING, sMessage
End Sub
Public Sub LogError(sMessage As String)
WriteLog LOG_LEVEL_ERROR, sMessage
End Sub
Public Sub LogCritical(sMessage As String)
WriteLog LOG_LEVEL_CRITICAL, sMessage
End Sub2. ���ݰ�����
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
' ���ݰ����Կ���
Private m_bDebugPackets As Boolean
Public Sub EnablePacketDebug(bEnable As Boolean)
m_bDebugPackets = bEnable
End Sub
' �����¼�����
Private Sub mbMaster_OnDataReceived(Data() As Byte)
If Not m_bDebugPackets Then Exit Sub
Dim sHex As String
Dim i As Long
For i = 0 To UBound(Data)
sHex = sHex & Format$(Data(i), "00") & " "
Next i
Debug.Print "RX [" & Format$(Now, "hh:mm:ss") & "] " & sHex
LogInfo "RX: " & sHex
End Sub
' �������ݰ�����
Private Sub DebugSendPacket(ByRef Data() As Byte)
If Not m_bDebugPackets Then Exit Sub
Dim sHex As String
Dim i As Long
For i = 0 To UBound(Data)
sHex = sHex & Format$(Data(i), "00") & " "
Next i
Debug.Print "TX [" & Format$(Now, "hh:mm:ss") & "] " & sHex
LogInfo "TX: " & sHex
End Sub
' ����Modbus���ݰ�
Public Sub ParseModbusPacket(ByRef Data() As Byte)
If UBound(Data) < 2 Then Exit Sub
Dim bFC As Byte
bFC = Data(1)
Debug.Print "=== Modbus Packet ==="
Debug.Print "Slave ID: " & Data(0)
Debug.Print "Function Code: 0x" & Hex$(bFC) & " (" & GetFunctionName(bFC) & ")"
Select Case bFC
Case &H1 ' Read Coils
If UBound(Data) >= 3 Then
Debug.Print "Byte Count: " & Data(2)
End If
Case &H3 ' Read Holding Registers
If UBound(Data) >= 3 Then
Dim iByteCount As Long
iByteCount = Data(2)
Debug.Print "Byte Count: " & iByteCount
Dim i As Long
For i = 0 To (iByteCount \ 2) - 1
Dim iReg As Integer
iReg = Data(3 + i * 2) * 256 + Data(4 + i * 2)
Debug.Print " Reg[" & i & "] = " & iReg & " (0x" & Hex$(iReg) & ")"
Next i
End If
End Select
Debug.Print "====================="
End Sub
Private Function GetFunctionName(bFC As Byte) As String
Select Case bFC
Case &H1: GetFunctionName = "Read Coils"
Case &H2: GetFunctionName = "Read Discrete Inputs"
Case &H3: GetFunctionName = "Read Holding Registers"
Case &H4: GetFunctionName = "Read Input Registers"
Case &H5: GetFunctionName = "Write Single Coil"
Case &H6: GetFunctionName = "Write Single Register"
Case &HF: GetFunctionName = "Write Multiple Coils"
Case &H10: GetFunctionName = "Write Multiple Registers"
Case Else: GetFunctionName = "Unknown"
End Select
End Function��ȫ����
1. ������֤
vb
Option Explicit
Private Type AuthConfig
Enabled As Boolean
Username As String
Password As String
Token As String
TokenExpiry As Date
End Type
Private m_Auth As AuthConfig
' ��ʼ����֤
Public Sub SetAuthentication(bEnabled As Boolean, sUser As String, sPass As String)
m_Auth.Enabled = bEnabled
m_Auth.Username = sUser
m_Auth.Password = sPass
m_Auth.Token = ""
m_Auth.TokenExpiry = #1/1/1900#
End Sub
' �����֤
Public Function CheckAuthentication() As Boolean
If Not m_Auth.Enabled Then
CheckAuthentication = True ' δ������֤,ֱ��ͨ��
Exit Function
End If
' ���Token�Ƿ����
If m_Auth.Token <> "" And Now < m_Auth.TokenExpiry Then
CheckAuthentication = True
Exit Function
End If
' ִ����֤
CheckAuthentication = Authenticate()
End Function
' ִ����֤
Private Function Authenticate() As Boolean
' ����ʵ��ʵ�ʵ���֤��
' ������ͨ��Modbus������֤����
' ����ͨ������������֤
' ʾ��: ͨ��Modbus�ض��Ĵ�����֤
Dim bSuccess As Boolean
On Error Resume Next
' ������֤��Ϣ
Dim iUserHash As Integer
Dim iPassHash As Integer
iUserHash = SimpleHash(m_Auth.Username)
iPassHash = SimpleHash(m_Auth.Password)
' д����֤�Ĵ���
mbMaster.WriteSingleRegister 998, iUserHash
mbMaster.WriteSingleRegister 999, iPassHash
' ��ȡ��֤���
Dim iResult() As Integer
iResult = mbMaster.ReadHoldingRegisters(1000, 1)
If UBound(iResult) >= 0 Then
If iResult(0) = 1 Then
' ��֤�ɹ�
m_Auth.Token = GenerateToken()
m_Auth.TokenExpiry = DateAdd("h", 1, Now) ' 1Сʱ����
Authenticate = True
Debug.Print "Authentication successful"
Exit Function
End If
End If
Authenticate = False
Debug.Print "Authentication failed"
End Function
' ��ϣ(��ʾ��,ʵ��Ӧ��Ӧʹ�ø���ȫ�Ĺ�ϣ)
Private Function SimpleHash(sInput As String) As Integer
Dim i As Long
Dim lHash As Long
For i = 1 To Len(sInput)
lHash = lHash + Asc(Mid$(sInput, i, 1)) * (i Mod 7 + 1)
Next i
SimpleHash = lHash And &HFFFF
End Function
' ����Token(��ʾ��)
Private Function GenerateToken() As String
GenerateToken = "TOKEN_" & Format$(Now, "yyyymmddhhmmss")
End Function2. ���ݼ���(ʾ��)
vb
Option Explicit
Private m_bEncryptData As Boolean
Private m_EncryptionKey As String
' �������ݼ���
Public Sub EnableEncryption(bEnable As Boolean, sKey As String)
m_bEncryptData = bEnable
m_EncryptionKey = sKey
End Sub
' ��������(��XOR����,��ʾ��)
Public Function EncryptData(ByRef Data() As Byte) As Byte()
If Not m_bEncryptData Then
EncryptData = Data
Exit Function
End If
Dim baEncrypted() As Byte
ReDim baEncrypted(UBound(Data)) As Byte
Dim i As Long
Dim lKeyLen As Long
lKeyLen = Len(m_EncryptionKey)
For i = 0 To UBound(Data)
Dim bKeyByte As Byte
bKeyByte = Asc(Mid$(m_EncryptionKey, (i Mod lKeyLen) + 1, 1))
baEncrypted(i) = Data(i) Xor bKeyByte
Next i
EncryptData = baEncrypted
End Function
' ��������
Public Function DecryptData(ByRef Data() As Byte) As Byte()
' XOR�����ǶԳƵ�,���ܺͼ�����ͬ
DecryptData = EncryptData(Data)
End Functionʵ��Ӧ�ó���
1. ��ҵ���ݲɼ�
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
Private WithEvents tmrCollect As Timer
' ���ݲɼ�����
Private Type DataPoint
RegisterAddr As Long
Name As String
Unit As String
MinValue As Single
MaxValue As Single
AlarmHigh As Single
AlarmLow As Single
LastValue As Single
LastUpdate As Date
End Type
Private m_DataPoints() As DataPoint
' ��ʼ�����ݵ�
Public Sub InitializeDataPoints()
ReDim m_DataPoints(9) As DataPoint
' �¶�
m_DataPoints(0).RegisterAddr = 0
m_DataPoints(0).Name = "�¶�"
m_DataPoints(0).Unit = "��C"
m_DataPoints(0).MinValue = -50
m_DataPoints(0).MaxValue = 150
m_DataPoints(0).AlarmHigh = 120
m_DataPoints(0).AlarmLow = -20
' ѹ��
m_DataPoints(1).RegisterAddr = 1
m_DataPoints(1).Name = "ѹ��"
m_DataPoints(1).Unit = "kPa"
m_DataPoints(1).MinValue = 0
m_DataPoints(1).MaxValue = 10000
m_DataPoints(1).AlarmHigh = 8000
m_DataPoints(1).AlarmLow = 0
' ����
m_DataPoints(2).RegisterAddr = 2
m_DataPoints(2).Name = "����"
m_DataPoints(2).Unit = "L/min"
m_DataPoints(2).MinValue = 0
m_DataPoints(2).MaxValue = 1000
m_DataPoints(2).AlarmHigh = 900
m_DataPoints(2).AlarmLow = 0
' ... �������ݵ�
End Sub
' ���ݲɼ�ѭ��
Private Sub tmrCollect_Timer()
On Error Resume Next
Dim i As Long
Dim iRegs() As Integer
' ������ȡ���мĴ���
iRegs = mbMaster.ReadHoldingRegisters(0, 10)
If UBound(iRegs) >= 0 Then
For i = 0 To UBound(m_DataPoints)
If i <= UBound(iRegs) Then
Dim fValue As Single
' ת��ԭʼֵ
fValue = ConvertRawValue(iRegs(i), m_DataPoints(i))
' �������ݵ�
m_DataPoints(i).LastValue = fValue
m_DataPoints(i).LastUpdate = Now
' ��鱨��
CheckAlarm i, fValue
' ��¼����
LogDataPoint i, fValue
' ����UI��ʾ
UpdateDataPointUI i, fValue
End If
Next i
End If
End Sub
' ת��ԭʼֵ
Private Function ConvertRawValue(iRaw As Integer, pt As DataPoint) As Single
' ���ݵ�λת��ԭʼֵ
Select Case pt.Unit
Case "��C"
' �¶�: ֱ��ʹ��
ConvertRawValue = CSng(iRaw)
Case "kPa"
' ѹ��: ԭʼֵ * 10
ConvertRawValue = CSng(iRaw) * 10
Case "L/min"
' ����: ԭʼֵ * 0.1
ConvertRawValue = CSng(iRaw) * 0.1
Case Else
ConvertRawValue = CSng(iRaw)
End Select
End Function
' ��鱨��
Private Sub CheckAlarm(Index As Long, fValue As Single)
With m_DataPoints(Index)
If fValue >= .AlarmHigh Then
TriggerAlarm .Name, "�߱���", fValue, .AlarmHigh
ElseIf fValue <= .AlarmLow Then
TriggerAlarm .Name, "�ͱ���", fValue, .AlarmLow
End If
End With
End Sub
' ��������
Private Sub TriggerAlarm(sPointName As String, sAlarmType As String, fValue As Single, fThreshold As Single)
Dim sMessage As String
sMessage = sPointName & " " & sAlarmType & ": " & Format$(fValue, "0.00") & _
" (��ֵ: " & Format$(fThreshold, "0.00") & ")"
Debug.Print "[ALARM] " & Format$(Now, "hh:mm:ss") & " - " & sMessage
' �����ʼ�֪ͨ
' SendAlarmEmail sMessage
' ��¼������־
' LogAlarm sMessage
' ���±���UI
' UpdateAlarmUI sPointName, sAlarmType, sMessage
End Sub
' ��¼���ݵ�
Private Sub LogDataPoint(Index As Long, fValue As Single)
With m_DataPoints(Index)
' ���浽���ݿ���ļ�
Dim sLine As String
sLine = Format$(Now, "yyyy-mm-dd hh:mm:ss") & "," & _
.Name & "," & _
Format$(fValue, "0.00") & "," & _
.Unit
' WriteToFile sLine
End With
End Sub2. �豸����
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
' �豸��������
Public Enum DeviceCommand
CMD_START = 1
CMD_STOP = 2
CMD_RESET = 3
CMD_EMERGENCY_STOP = 99
End Enum
' ������Ȧӳ��
Private Type CoilMapping
Command As DeviceCommand
Address As Long
Value As Boolean
End Type
Private m_CoilMappings() As CoilMapping
' ��ʼ������ӳ��
Public Sub InitializeControlMappings()
ReDim m_CoilMappings(3) As CoilMapping
m_CoilMappings(0).Command = CMD_START
m_CoilMappings(0).Address = 0
m_CoilMappings(0).Value = True
m_CoilMappings(1).Command = CMD_STOP
m_CoilMappings(1).Address = 1
m_CoilMappings(1).Value = True
m_CoilMappings(2).Command = CMD_RESET
m_CoilMappings(2).Address = 2
m_CoilMappings(2).Value = True
m_CoilMappings(3).Command = CMD_EMERGENCY_STOP
m_CoilMappings(3).Address = 10
m_CoilMappings(3).Value = True
End Sub
' ִ���豸��������
Public Function ExecuteCommand(Cmd As DeviceCommand) As Boolean
On Error GoTo ErrorHandler
Dim i As Long
Dim bFound As Boolean
' ��������ӳ��
For i = 0 To UBound(m_CoilMappings)
If m_CoilMappings(i).Command = Cmd Then
bFound = True
Exit For
End If
Next i
If Not bFound Then
Debug.Print "Command not found: " & Cmd
ExecuteCommand = False
Exit Function
End If
' д�������Ȧ
Dim bSuccess As Boolean
bSuccess = mbMaster.WriteSingleCoil(m_CoilMappings(i).Address, m_CoilMappings(i).Value)
If bSuccess Then
Debug.Print "Command executed: " & GetCommandName(Cmd)
ExecuteCommand = True
Else
Debug.Print "Command failed: " & GetCommandName(Cmd)
ExecuteCommand = False
End If
Exit Function
ErrorHandler:
Debug.Print "ExecuteCommand error: " & Err.Description
ExecuteCommand = False
End Function
' ��ȡ��������
Private Function GetCommandName(Cmd As DeviceCommand) As String
Select Case Cmd
Case CMD_START: GetCommandName = "����"
Case CMD_STOP: GetCommandName = "ֹͣ"
Case CMD_RESET: GetCommandName = "��λ"
Case CMD_EMERGENCY_STOP: GetCommandName = "����ֹͣ"
Case Else: GetCommandName = "δ֪"
End Select
End Function
' ��ȫ����:��ȷ�ϵĿ���
Public Function SafeExecuteCommand(Cmd As DeviceCommand, bRequireConfirm As Boolean) As Boolean
' �����Ҫȷ��
If bRequireConfirm Then
Dim iResponse As Integer
iResponse = MsgBox("ȷ��Ҫִ������: " & GetCommandName(Cmd) & "?", _
vbQuestion + vbYesNo + vbDefaultButton2, _
"ȷ�ϲ���")
If iResponse <> vbYes Then
Debug.Print "Command cancelled by user"
SafeExecuteCommand = False
Exit Function
End If
End If
' ִ������
Dim bResult As Boolean
bResult = ExecuteCommand(Cmd)
' ��¼������־
LogCommand Cmd, bResult
SafeExecuteCommand = bResult
End Function
' ��¼������־
Private Sub LogCommand(Cmd As DeviceCommand, bSuccess As Boolean)
Dim sStatus As String
sStatus = IIf(bSuccess, "�ɹ�", "ʧ��")
Dim sLine As String
sLine = Format$(Now, "yyyy-mm-dd hh:mm:ss") & "," & _
GetCommandName(Cmd) & "," & _
sStatus
' д����־�ļ�
On Error Resume Next
Dim iFile As Integer
iFile = FreeFile
Open App.Path & "\command_log.csv" For Append As #iFile
Print #iFile, sLine
Close #iFile
On Error GoTo 0
End Sub3. ��������
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster ' �������
Private WithEvents mbSlave As cModbusSlave ' �������
' ����ӳ������
Private Type DataMapping
MasterAddr As Long ' ��λ����ַ
SlaveAddr As Long ' ��λ����ַ
Direction As Integer ' 0=Master��Slave, 1=Slave��Master
ScaleFactor As Single ' ��������
Offset As Single ' ƫ����
Enabled As Boolean
End Type
Private m_Mappings() As DataMapping
' ��ʼ������ӳ��
Public Sub InitializeDataMappings()
ReDim m_Mappings(4) As DataMapping
' ӳ��1: ��λ���¶� -> ��λ���Ĵ���
m_Mappings(0).MasterAddr = 0
m_Mappings(0).SlaveAddr = 100
m_Mappings(0).Direction = 1 ' Slave��Master
m_Mappings(0).ScaleFactor = 1.0
m_Mappings(0).Offset = 0
m_Mappings(0).Enabled = True
' ӳ��2: ��λ���趨ֵ -> ��λ���Ĵ���
m_Mappings(1).MasterAddr = 10
m_Mappings(1).SlaveAddr = 200
m_Mappings(1).Direction = 0 ' Master��Slave
m_Mappings(1).ScaleFactor = 10.0
m_Mappings(1).Offset = 0
m_Mappings(1).Enabled = True
' ... ����ӳ��
End Sub
' ����ͬ��
Public Sub SyncGateway()
On Error Resume Next
Dim i As Long
For i = 0 To UBound(m_Mappings)
If Not m_Mappings(i).Enabled Then GoTo NextMapping
Select Case m_Mappings(i).Direction
Case 0 ' Master��Slave
SyncMasterToSlave i
Case 1 ' Slave��Master
SyncSlaveToMaster i
End Select
NextMapping:
Next i
End Sub
' ͬ��Master��Slave
Private Sub SyncMasterToSlave(Index As Long)
Dim iMasterReg As Integer
iMasterReg = mbMaster.ReadHoldingRegisters(m_Mappings(Index).MasterAddr, 1)(0)
Dim fScaled As Single
fScaled = (CSng(iMasterReg) * m_Mappings(Index).ScaleFactor) + m_Mappings(Index).Offset
' �Slave
mbSlave.SetHoldingRegister m_Mappings(Index).SlaveAddr, CInt(fScaled)
Debug.Print "Sync M[" & m_Mappings(Index).MasterAddr & "] -> S[" & _
m_Mappings(Index).SlaveAddr & "]: " & fScaled
End Sub
' ͬ��Slave��Master
Private Sub SyncSlaveToMaster(Index As Long)
Dim iSlaveReg As Integer
iSlaveReg = mbSlave.GetHoldingRegister(m_Mappings(Index).SlaveAddr)
Dim fScaled As Single
fScaled = (CSng(iSlaveReg) * m_Mappings(Index).ScaleFactor) + m_Mappings(Index).Offset
' �Master
mbMaster.WriteSingleRegister m_Mappings(Index).MasterAddr, CInt(fScaled)
Debug.Print "Sync S[" & m_Mappings(Index).SlaveAddr & "] -> M[" & _
m_Mappings(Index).MasterAddr & "]: " & fScaled
End Sub��������
Q1: ������������?
����:
- ������ȡ,ÿ�β��������Ĵ�����(125)
- ʹ�ö�ʱ����ʱ�ζ�ȡ
- ʵ�����ݻ������
vb
' ������ȡ��������
Public Sub ReadLargeData(StartAddr As Long, TotalCount As Long)
Dim lOffset As Long
Dim lRemaining As Long
Dim lBatchSize As Long
Dim iBatch As Long
lBatchSize = mbMaster.Defaults.MAX_REGISTERS
lOffset = 0
lRemaining = TotalCount
Do While lRemaining > 0
Dim lThisBatch As Long
lThisBatch = IIf(lRemaining > lBatchSize, lBatchSize, lRemaining)
Dim iRegs() As Integer
iRegs = mbMaster.ReadHoldingRegisters(StartAddr + lOffset, lThisBatch)
' ��������
ProcessBatch iRegs, lOffset
lOffset = lOffset + lThisBatch
lRemaining = lRemaining - lThisBatch
iBatch = iBatch + 1
Debug.Print "Batch " & iBatch & " completed"
' �����������
Sleep 10
Loop
End SubQ2: ���ʵ���ȱ���?
����:
- ͬʱ����������̨�豸
- ���豸����ʱ��ȡ���豸
- ���豸����ʱ�Զ��л��������豸
vb
Option Explicit
Private WithEvents mbMasterPrimary As cModbusMaster
Private WithEvents mbMasterBackup As cModbusMaster
Private m_UsePrimary As Boolean
' �л��������豸
Private Sub SwitchToBackup()
On Error Resume Next
mbMasterPrimary.Disconnect
Dim iRegs() As Integer
iRegs = mbMasterBackup.ReadHoldingRegisters(0, 1)
If UBound(iRegs) >= 0 Then
m_UsePrimary = False
Debug.Print "Switched to backup device"
RaiseEvent DeviceSwitched("Backup")
Else
Debug.Print "Backup device also failed"
RaiseEvent AllDevicesFailed
End If
End Sub
' �����л������豸
Private Sub TrySwitchBack()
On Error Resume Next
mbMasterPrimary.Connect
Dim iRegs() As Integer
iRegs = mbMasterPrimary.ReadHoldingRegisters(0, 1)
If UBound(iRegs) >= 0 Then
m_UsePrimary = True
Debug.Print "Switched back to primary device"
RaiseEvent DeviceSwitched("Primary")
End If
End Sub
' ���ܶ�ȡ(�Զ�ѡ���豸)
Public Function SmartRead(StartAddr As Long, Quantity As Long) As Integer()
Dim iRegs() As Integer
If m_UsePrimary Then
' ���Դ����豸��ȡ
On Error Resume Next
iRegs = mbMasterPrimary.ReadHoldingRegisters(StartAddr, Quantity)
If UBound(iRegs) < 0 Then
' ���豸ʧ��,�л�������
SwitchToBackup
If Not m_UsePrimary Then
iRegs = mbMasterBackup.ReadHoldingRegisters(StartAddr, Quantity)
End If
End If
Else
' �ӱ����豸��ȡ
iRegs = mbMasterBackup.ReadHoldingRegisters(StartAddr, Quantity)
' ���ڳ����л������豸
Static lSwitchCount As Long
lSwitchCount = lSwitchCount + 1
If lSwitchCount > 10 Then
TrySwitchBack
lSwitchCount = 0
End If
End If
SmartRead = iRegs
End FunctionQ3: ���ʵ�ֶ�������?
vb
Option Explicit
Private WithEvents mbMaster As cModbusMaster
Private WithEvents tmrReconnect As Timer
Private m_bAutoReconnect As Boolean
Private m_lReconnectInterval As Long ' ����
' �����Զ�����
Public Sub EnableAutoReconnect(bEnable As Boolean, lInterval As Long)
m_bAutoReconnect = bEnable
m_lReconnectInterval = lInterval
Set tmrReconnect = New Timer
tmrReconnect.Interval = lInterval
End Sub
Private Sub mbMaster_OnDisconnect()
If m_bAutoReconnect Then
Debug.Print "Disconnected, will reconnect in " & (m_lReconnectInterval \ 1000) & "s"
tmrReconnect.Enabled = True
End If
End Sub
Private Sub tmrReconnect_Timer()
tmrReconnect.Enabled = False
On Error Resume Next
mbMaster.Connect
If Err.Number = 0 And mbMaster.State = MB_MASTER_STATE_CONNECTED Then
Debug.Print "Reconnected successfully"
Else
' ����ʧ��,��������
tmrReconnect.Enabled = True
End If
End Sub��һ��
- �鿴 master.md �˽���վ��ϸ API
- �鿴 slave.md �˽��վ��ϸ API
- �鿴 quickstart.md ��������
- �鿴 overview.md �������
������: 2026-01-16
������־
2026-01-16 (v1.1.0)
- �����豸���ýṹ�壬ʹ��
ModbusMasterProtocolType - ����
BindAddress����ʾ������վ�� - ����ö�����ã����� v1.1.0 �����淶��