2017-02-08 266 views
1

来自密歇根州的问候,调用堆栈大小?

我有一个程序连续运行(日志数据),它在一个Sub(“日志数据”子)内的While循环内运行,然后当日志文件变为“完整”跳到另一个Sub创建一个新文件,然后跳回到“日志数据”子继续。无论如何,它一直这样做,并可以像这样运行数小时,创建100个以上的文件或更多的数据。问题是程序在某个时候崩溃了,并且在程序的这个部分中它总是崩溃(这两个子目录之一,尽管我没有精确指出哪一个。)当我在部署程序的机器上运行调试器,Call Stack非常大,我想知道这是否是一个问题,以及如何进行管理。这可能是程序崩溃的原因(调用堆栈变得太大?)。我得到了某种内存异常至少在其中一次崩溃时发生错误我昨天对代码进行了一些编辑,试图缓解这种情况,最后一次崩溃(今天早上进入办公室时)我遇到了一个null引用异常错误,尽管我不能查明除非我在调试模式下从我的开发机器运行程序,我打算在下一步做什么以确切地捕捉到这两个Subs中的任何一个发生崩溃的代码行,我需要在夜间像我一样运行它说,该程序可以在发生崩溃之前运行数小时尼韦尔,问题是关于调用堆栈。大型调用堆栈有问题吗?这是如何管理/清除?

感谢, d

Public Sub dataAcquiring() 
    'Receive the collection of channels in this sub and append data to each of the channels 
    'Set up the channel group 
    Dim message1 As String = "The data file may have been deleted or moved prior to a new data file and directory being created. Check the main 'Data' directory and be sure the file exists, or simply create a new data file." 
    Dim testBool As Boolean = False 

    'Set the global variable to True if running the application from the development machine in debug mode. Otherwise, initialize it to false for deployment. 
    If Connectlocal = True Then 
     statsFile = "C:\Users\dmckin01\Desktop\Data\" & folderName & "\" & dataFileName & "_stats.csv" 
    Else : statsFile = "D:\Data\" & folderName & "\" & dataFileName & "_stats.csv" 
    End If 

    Try 
     logFile.Open() 
    Catch ex As Exception 
     MessageBox.Show(Me, ex.Message & message1, "File not found", MessageBoxButtons.OK, MessageBoxIcon.Error) 
     cbRecord.Checked = False 
     Return 
    End Try 

    Dim i As Integer = 0, n As Integer = 0, hvar As Integer, value As Single, count As Integer = 0, maxValue As Single 
    Dim b As Boolean = False, returnValue As Type, stringVar As String, lastValidNumber As Integer 
    Dim dtype As System.Type 
    Dim channelGroupName As String = "Main Group" 
    Dim channelGroup As TdmsChannelGroup = New TdmsChannelGroup(channelGroupName) 
    Dim channelGroups As TdmsChannelGroupCollection = logFile.GetChannelGroups() 

    If (channelGroups.Contains(channelGroupName)) Then 
     channelGroup = channelGroups(channelGroupName) 
    Else 
     channelGroups.Add(channelGroup) 
    End If 

    'Set up the TDMS channels 
    Dim Names As String() = New String(13) {" Spindle Speed (rpm) ", " Oil Flow (ccm) ", " Torque (Nm) ", " LVDT Displacement (mm) ", " Linear Pot Displacement (mm) ", _ 
                " Pneu. Actuator (0=OFF, 1=ON) ", " Elec. Actuator (0=OFF, 1=ON) ", " Hydr. Actuator (0=OFF, 1=ON) ", _ 
                " Upper Tank Oil Temp. (°F) ", " Lower Tank Oil Temp. (°F) ", " Exit Oil Temp. (°F) ", _ 
                " Inlet Oil Temp. (°F) ", " Part Temp. (°F) ", " Time Stamp "} 
    Dim dataArrayNames As String() = New String(13) {"arrSpeed", "arrFlow", "arrTorque", "arrLVDT", "arrLinPot", "arrActPneu", "arrActElec", "arrActHydr", _ 
                "arrUpperOil", "arrLowerOil", "arrExitOil", "arrInletOil", "arrTestPart", "arrTimeStamp"} 

    Dim OPCTagNames As String() = New String(13) {"peakTorque", "peakTorqueSpeed", "peakTorquePlatePos", "timeToPeakTorque", "firstPeakTorque", "firstPeakTorqueSpeed", _ 
               "firstPeakTorquePlatePos", "timeToFirstPeakTorque", "peakDecel", "peakJerk", "engagementSpeed", "slidePlateSpeed", _ 
               "timeOfEngagement", "totalEnergy"} 


    Dim bools As Boolean() = New Boolean(13) {recSpeed, recOilFlow, recTorque, recLVDT, recLinPot, recActPneu, recActElec, recActHydr, recUpperOil, recLowerOil, _ 
               recExitOil, recInletOil, recTestPart, recTimeStamp} 

    'Instantiate the TDMS channels to be used. We have to do this each and every time this Sub is executed because National Instruments 
    'does not have a method to 'clear' the channel group. 
    Dim ch0 As TdmsChannel = New TdmsChannel(Names(0), TdmsDataType.Float) 'spindle speed 
    Dim ch1 As TdmsChannel = New TdmsChannel(Names(1), TdmsDataType.Float) 'oil flow 
    Dim ch2 As TdmsChannel = New TdmsChannel(Names(2), TdmsDataType.Float) 'torque 
    Dim ch3 As TdmsChannel = New TdmsChannel(Names(3), TdmsDataType.Float) 'actuator position (LVDT) 
    Dim ch4 As TdmsChannel = New TdmsChannel(Names(4), TdmsDataType.Float) 'actuator position (LINEAR POT) 
    Dim ch5 As TdmsChannel = New TdmsChannel(Names(5), TdmsDataType.Float) 'actuator state (pneu) 
    Dim ch6 As TdmsChannel = New TdmsChannel(Names(6), TdmsDataType.Float) 'actuator state (elec) 
    Dim ch7 As TdmsChannel = New TdmsChannel(Names(7), TdmsDataType.Float) 'actuator state (hydr) 
    Dim ch8 As TdmsChannel = New TdmsChannel(Names(8), TdmsDataType.Float) 'upper oil tank temp 
    Dim ch9 As TdmsChannel = New TdmsChannel(Names(9), TdmsDataType.Float) 'lower oil tank temp 
    Dim ch10 As TdmsChannel = New TdmsChannel(Names(10), TdmsDataType.Float) 'Exit oil tank temp 
    Dim ch11 As TdmsChannel = New TdmsChannel(Names(11), TdmsDataType.Float) 'Inlet oil temp 
    Dim ch12 As TdmsChannel = New TdmsChannel(Names(12), TdmsDataType.Float) 'Part temp 
    Dim ch13 As TdmsChannel = New TdmsChannel(Names(13), TdmsDataType.String) 'Time stamp 
    Dim Channels As TdmsChannelCollection 
    Dim chans As TdmsChannel() = New TdmsChannel(13) {ch0, ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8, ch9, ch10, ch11, ch12, ch13} 
    Channels = channelGroup.GetChannels() 

    ch0.UnitString = "RPM" : ch0.Description = "Rotational speed of the spindle shaft." 
    ch1.UnitString = "CCM" : ch1.Description = "Oil flow from the specimen pump." 
    ch2.UnitString = "Nm" : ch2.Description = "Torque from the torque cell." 
    ch3.UnitString = "mm" : ch3.Description = "Linear displacement of the linear velocity displacement transducer." 
    ch4.UnitString = "mm" : ch4.Description = "Linear displacement of the linear potentiometer." 
    ch5.UnitString = "BIT" : ch5.Description = "Binary state of the pneumatic actuator (0=OFF, 1=ON)." 
    ch6.UnitString = "BIT" : ch6.Description = "Binary state of the electric actuator (0=OFF, 1=ON)." 
    ch7.UnitString = "BIT" : ch7.Description = "Binary state of the hydraulic actuator (0=OFF, 1=ON)." 
    ch8.UnitString = "°F" : ch8.Description = "Upper tubular tank oil temperature." 
    ch9.UnitString = "°F" : ch9.Description = "Lower (main) tank oil temperature." 
    ch10.UnitString = "°F" : ch10.Description = "Thermocouple (Location: Remote rack, EL3318, Ch.2)." 
    ch11.UnitString = "°F" : ch11.Description = "Thermocouple (Location: Remote rack, EL3318, Ch.3)." 
    ch12.UnitString = "°F" : ch12.Description = "Thermocouple (Location: Remote rack, EL3318, Ch.1)" 
    ch13.UnitString = "nS" : ch13.Description = "Time when the data was captured." 

    'The only TDMS channels that get added to the collection are the ones that the user selects on the 'Configure Data File' form. 
    'That is what this If-Then block is for. 
    If Channels.Count = 0 Then 
     jArray.Clear() 
     plcArrayNames.Clear() 
     For Each [boolean] In bools 
      If [boolean] = True Then 
       Channels.Add(chans(i)) 
       Channels = channelGroup.GetChannels 'new 
       jArray.Add(jaggedarray(i)) 
       plcArrayNames.Add(dataArrayNames(i)) 
      End If 
      i += 1 
     Next 
    End If 

    'At this point, we are ready to write data to the TDMS file. 
    'Establish the line of communication to the PLC so we can read the data arrays. 
    Dim tcClient As New TwinCAT.Ads.TcAdsClient() 
    Dim dataStreamRead As TwinCAT.Ads.AdsStream = New AdsStream 
    Dim binaryReader As System.IO.BinaryReader = New BinaryReader(dataStreamRead) 

    If Connectlocal = True Then 
     tcClient.Connect(851) 'local 
    Else : tcClient.Connect(AMSNetID, 851) 
    End If 

    While cbRecord.Checked = True 
     b = tcClient2.ReadAny(DRHvar, GetType(Boolean)) 'read the handshaking variable from the PLC 
     If b = False Then 
      'This For loop reads the appropriate arrays in the PLC and then writes that data to the appropriate arrays here. 
      'The arrays in here will eventually get written to the TDMS file. 
      i = 0 
      n = 0 
      writingData = True 
      For Each [string] In dataArrayNames 
       If dataArrayNames(n) = plcArrayNames(i) Then 
        hvar = tcClient.CreateVariableHandle("IO_HS.Data." & dataArrayNames(n)) 
        value = 0 
        returnValue = jArray(i).GetType 
        If returnValue.Name = "Single[]" Then 
         dataStreamRead.SetLength(jArray(0).Length * 4) 
         dataStreamRead.Position = 0 
         tcClient.Read(hvar, dataStreamRead) 
         For Each [element] In jArray(0) 
          jArray(i)(value) = binaryReader.ReadSingle() 
          value += 1 
         Next 
        ElseIf returnValue.Name = "Int64[]" Then 
         dataStreamRead.SetLength(jArray(0).Length * 8) 
         dataStreamRead.Position = 0 
         tcClient.Read(hvar, dataStreamRead) 
         For Each [element] In jArray(0) 
          jArray(i)(value) = binaryReader.ReadInt64() 
          value += 1 
         Next 
        ElseIf returnValue.Name = "String[]" Then 
         dataStreamRead.SetLength(jArray(0).Length * 32) 
         dataStreamRead.Position = 0 
         tcClient.Read(hvar, dataStreamRead) 
         For Each [element] In jArray(0) 
          stringVar = binaryReader.ReadChars(32) 
          lastValidNumber = Math.Max(Math.Max(Math.Max(Math.Max(Math.Max(Math.Max(Math.Max(Math.Max(Math.Max(stringVar.LastIndexOf("0"), stringVar.LastIndexOf("1")), stringVar.LastIndexOf("2")), stringVar.LastIndexOf("3")), stringVar.LastIndexOf("4")), stringVar.LastIndexOf("5")), stringVar.LastIndexOf("6")), stringVar.LastIndexOf("7")), stringVar.LastIndexOf("8")), stringVar.LastIndexOf("9")) 
          If lastValidNumber > 0 Then 
           jArray(i)(value) = stringVar.Substring(0, lastValidNumber + 1) 
          Else 
           jArray(i)(value) = "Invalid Timestamp" 
          End If 
          value += 1 
         Next 
        End If 
        tcClient.DeleteVariableHandle(hvar) 
        i += 1 
        If i = plcArrayNames.Count Then 
         Exit For 
        End If 
       End If 
       n += 1 
      Next 

      'This For loop appends/writes the data from each array to the actual TDMS file. 
      i = 0 
      For Each [array] In jArray 
       dtype = Channels(i).GetDataType 
       If dtype.Name = "Int32" Then 
        Channels(i).AppendData(Of Integer)(jArray(i)) 
       ElseIf dtype.Name = "Single" Then 
        Channels(i).AppendData(Of Single)(jArray(i)) 
       ElseIf dtype.Name = "Boolean" Then 
        Channels(i).AppendData(Of Boolean)(jArray(i)) 
       ElseIf dtype.Name = "String" Then 
        Channels(i).AppendData(Of String)(jArray(i)) 
       End If 
       i += 1 
      Next 

      Try 
       'Call the DataAnalyzer dll to write stats of the cycle to stats CSV file. Also plot the data of the cycle on the chart on the UI 
       Invoke(Sub() DataAnalyzer.Analyze(arrSpeed, arrTorque, arrLinPot)) 
       Invoke(Sub() plotData()) 
       Invoke(Sub() DataAnalyzer.WriteData(statsFile, logFile.Path, arrTimeStamp(0), plcData.cyclesCompleted)) 
      Catch ex As Exception 
       testBool = True 
      End Try 

      'Populate the datagridview cells with the data values 
      dgvStats.Item(1, 0).Value = DataAnalyzer.peakTorque 
      dgvStats.Item(1, 1).Value = DataAnalyzer.engagementSpeed 
      dgvStats.Item(1, 2).Value = DataAnalyzer.slidePlateSpeed 
      dgvStats.Item(1, 3).Value = plcData.bimbaTravSpeed 
      dgvStats.Item(1, 4).Value = plcData.lastCycleTime 
      dgvStats.Item(1, 5).Value = plcData.currentCycleTime 
      dgvStats.Item(1, 6).Value = plcData.meanCycleTime 
      dgvStats.Item(1, 7).Value = plcData.cyclesPerHr 

      'NEW CODE to Evalute the elements in the arrTorque array to get the Max value recorded 
      maxValue = 0 
      For Each [element] In arrTorque 
       maxValue = Math.Max(maxValue, element) 
      Next 
      If maxValue <= plcData.torqueAlrmSP And plcData.cycleStarted Then 
       torqueLowCount += 1 
      Else : torqueLowCount = 0 
      End If 

      'Let the PLC know that we received the data and are now ready for the next set (handshaking variable). 
      tcClient2.WriteAny(DRHvar, True) 
     End If 

     'If the data count in the first column of the TDMS file exceeds the number here, then 
     'close the file and create a new one, then continue to append/write data 
     If Channels(0).DataCount >= 1020000 Then 
      For Each channel As TdmsChannel In chans 
       channel.Dispose() : channel = Nothing 
      Next 
      chans = Nothing 
      channelGroup.Dispose() : channelGroup = Nothing 
      If tcClient.IsConnected Then 
       dataStreamRead.Dispose() : dataStreamRead = Nothing 
       tcClient.Disconnect() : tcClient.Dispose() : tcClient = Nothing 
      End If 
      'Jump to the CreateNewFile Sub to create the next TDMS file 
      CreateNewFile() 
     End If 
    End While 

    If logFile.IsOpen = True Then 
     logFile.Close() 
    End If 
    If tcClient.IsConnected Then 
     dataStreamRead.Dispose() : dataStreamRead = Nothing 
     tcClient.Disconnect() : tcClient.Dispose() : tcClient = Nothing 
    End If 
    writingData = False 
End Sub 

Private Sub CreateNewFile() 
    'Create the new folder where the data file/s will reside 
    Dim newFilename As String = dataFileName & "_" & fileNum 
    Dim customFilePropertyNames() As String = {"Date"} 
    Dim customFilePropertyVals() As String = {""} 
    Dim newAuthor As String = logFile.Author 
    Dim newDescription As String = logFile.Description 
    Dim newTitle As String = logFile.Title 
    Dim newPath1 As String = "C:\Users\dmckin01\Desktop\Data\" & folderName 
    Dim newPath2 As String = "D:\Data\" & folderName 
    fileNum += 1 

    'Create the TDMS file and save it to the user specified directory 
    customFilePropertyVals(0) = Date.Today.ToShortDateString() 
    logFile.Close() 'Close the old logfile after we've gotten values/properties from it 
    logFile.Dispose() : logFile = Nothing 

    Try 
     If Connectlocal = True Then 
      logFile = New TdmsFile(newPath1 & "\" & newFilename & ".tdms", New TdmsFileOptions()) 
     Else : logFile = New TdmsFile(newPath2 & "\" & newFilename & ".tdms", New TdmsFileOptions()) 
     End If 
    Catch ex As Exception 
     MessageBox.Show("Directory not created. Make sure the TDMS file and/or directory that you are referencing are not already currently opened.", "Directory Creation Failed", MessageBoxButtons.OK, MessageBoxIcon.Error) 
     Exit Sub 
    End Try 
    logFile.Author = newAuthor 
    logFile.Description = newDescription 
    logFile.Title = newTitle 
    logFile.AddProperty(customFilePropertyNames(0), TdmsPropertyDataType.String, customFilePropertyVals(0)) 
    logFile.AutoSave = True 
    dataAcquiring() 
End Sub 
+0

两件事。 1)您应该将日志添加到您的应用程序中。 2)你有递归吗?如果是,请将其移除。 –

+0

已添加到原始文章中的代码。这两个子都包括在内。这在应用程序中的一个单独的线程中运行。 – busarider29

+0

无限递归似乎非常明显,因为您正在调用其他方法中的每个方法。在最后删除对'dataAcquiring()'的调用,'CreateNewFile'应该是一个返回文件名的函数。 – Slai

回答

1

堆栈错误总是在你的代码中的循环调用回自己造成的。通常由属性设置处理程序引发,这些处理程序设置了其他属性,然后轮流尝试设置初始属性。他们可能很难指出有时。

在你的情况,你都要求记录功能

dataAcquiring() 
End Sub 

在随后结束的文件创建程序的...这是一个严重的错误。

每次启动启动日志循环和旧的新实例的新文件仍然在栈上......直到它运行的房间

在这种情况下,它只是一个时间问题...创建例程应该只是退出。

但是,如果是我,我会让该代码的函数返回true或false。如果由于某种原因无法创建文件并在主循环中正常处理文件,它会返回false。

+0

感谢Trevor的指点和评论。日志文件只有在满时才会关闭,因此在实际写入数据时它会保持打开状态。一个“完整的”数据文件将是34个机器周期。大约每2.125分钟一个数据文件被填充,并且必须创建一个新的数据文件。那时我必须关闭日志文件。程序退出while循环的唯一时间是UI上的Record按钮被取消单击,或者由于当前日志文件已满而必须创建新的日志文件。我不得不重新实例化所有通道,因为NI缺乏API中的方法来实现它。 – busarider29

+0

OH..I错过了我对你的代码粗略研究的循环.... TMI .. LOL 这就是你的问题,每当你创建一个新文件时,你都会启动另一个记录器ad-infinitum。 –

+0

我想我没跟着。在dataAcquiring()的末尾,在它跳转到创建一个新文件之前,我正在杀死所有内容.....然后重新创建所有内容。 – busarider29