监视可执行文件的进度,以便在webform上报告

时间:2015-02-24 21:25:30

标签: c# asp.net webforms fortran legacy-code

我最近一直在考虑采用传统的建筑分析程序,并为其提供asp.NET Webforms和javascript的前端。 (遗留代码是用Fortran编写的,意图是在Web服务器或其他远程计算机上运行。)

问题:是否可以监控可执行文件的过程(在Web服务器或其他地方运行),以便可以在Webform上报告?

遗留代码仍然是正在进行的开发计划的一部分。我们不打算将其重新编码为C#等,因为:

1)表现至关重要;和

2)这将是一项庞大的事业。

但可以修改Fortran代码以输出内容或调用某些内容来更新进度,并让用户知道(希望通过Webform)可执行文件的工作方式。

有没有人遇到或应用过提供此功能的编程模型?

4 个答案:

答案 0 :(得分:1)

FORTRAN程序可以将状态(例如,2015年2月25日12:05:57开始,2015年2月25日12:08:38完成)写入文本文件或数据库表。在状态网页上,您可以使用自动刷新或jQuery ajax调用来检查进程的状态并进行更新。

答案 1 :(得分:1)

我发现在Fortran中执行I / O的最简单方法是使用其原生的.dat文件格式。它是二进制的,但它是一种相对理智的格式,允许存储具有任何类型数组数据的多个记录。要了解如何在Fortran上下文之外处理这些类型的文件,您可以查看following python script中的unpackNextRecord函数。这应该相对容易翻译成任何语言(如果它还没有存在的话)。

现在,在Fortran端,您可以使用类似

的功能创建这些文件
subroutine writeToFile[MyDataType](path, array) 
  implicit none 
  [MyDataType], intent(in), dimension(:) :: array 
  character(len=*), intent(in) :: path 
  character(len=:), allocatable :: dirname 
  integer(4) :: imt 

  dirname = getDirectory(path) 
  call makeDirectory(dirname) 
  call findNewFileHandle(imt) 
  open(imt, file = path, form = 'unformatted', status = 'replace') 
  write(imt) array 
  close(imt) 
  deallocate(dirname) 
end subroutine

您也可以使用不同的数据类型多次写入文件句柄,例如,如果您有一些字符串和要保存的数字数组 - 只需添加更多write(imt)个调用和输入你的助手子程序。要获取此处使用的辅助函数makeDirectoryfindNewFileHandle,请参阅the following Fortran 90 module

如果你习惯于处理文本文件,这个东西可能会有点奇怪,但是在Fortran中它更安全,更舒适 - 如果你想写纯文本,你会发现很多东西,直到你做对了,让我告诉你。

答案 2 :(得分:1)

我希望从C#调用长时间运行的Fortran例程,将Fortran例程编译为DLL,并将C#中的回调函数传递给Fortran例程。这样做的好处是您不会有多个进程争用同时访问同一个文件。

module MyLib

    implicit none

    interface
        subroutine ProgressUpdateAction(percentProgress)
            integer, intent(in) :: percentProgress
        end subroutine
    end interface   

contains

    subroutine DoWork(progressUpdate)

        !DIR$ ATTRIBUTES DLLEXPORT :: DoWork
        !DIR$ ATTRIBUTES ALIAS: 'DoWork' :: DoWork
        !DIR$ ATTRIBUTES REFERENCE :: progressCallBack

        procedure(ProgressUpdateAction), intent(in), pointer :: progressUpdate

        integer, parameter :: STEP_COUNT = 9
        integer :: i, percentProgress

        do i = 1, STEP_COUNT

            ! Update the status.
            percentProgress = (i - 1) * 100.0 / STEP_COUNT
            call progressUpdate(percentProgress)

            ! Do something expensive...

        end do

    end subroutine

end module

在C#调用例程中创建一个匹配的委托。记得通过引用传递所有内容 - Fortran默认值。

public static class FortranLib
{
    private const string _dllName = "FortranLib.dll";

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void ProgressUpdateAction(ref int progress); // Important the int is passed by ref (else we could use built-in Action<T> instead of delegate).

    [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    public static extern void DoWork([MarshalAs(UnmanagedType.FunctionPtr)] ref ProgressUpdateAction progressUpdate);
}

class Program
{
    static void Main(string[] args)
    {
        try
        {
            FortranLib.ProgressUpdateAction updateProgress = delegate(ref int p)
            {
                Console.WriteLine("Progress: " + p + "%");
            };

            FortranLib.DoWork(ref updateProgress);

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}

此处详细信息:http://www.luckingtechnotes.com/calling-fortran-from-c-monitoring-progress-using-callbacks/

答案 3 :(得分:0)

要制作一个不会影响代码性能的进度条,您可以执行以下操作。

  1. 在模块中创建全局变量

    module progress
      implicit none
      real*8           :: progress_value    = huge(1.d0)
      integer          :: progress_bar(2)   = (/ 1, 1 /)
      integer          :: progress_refresh  = 2
      integer          :: progress_unit     = 66
      logical          :: progress_active   = .False.
      character*(64)   :: progress_title    = "UNDEFINED"
      character*(128)  :: progress_filename = "/tmp/progress"
    end module
    
    • progress_value:您要监控的值(例如运行平均值)
    • progress_bar:第一个索引将包含当前循环计数,第二个索引将包含总循环计数。比率dble(progress_bar(1))/dble(progress_bar(2))将为您提供当前的进度百分比。
    • progress refresh:每次刷新之间的时间间隔(以秒为单位)
    • progress_unit:您将在其中写入信息的文件的单元号
    • 当您监控时,
    • progress_active.True.,否则为.False.
    • progress_title:让您确定您正在监控的内容。
    • progress_filename:您将在其中存储进度的文件的名称
  2. 创建例程以启动/停止监控

    初始化监控:

    subroutine start_progress(max,title,progress_init)
      use progress
      implicit none
      integer, intent(in)       :: max
      character*(*), intent(in) :: title
      real*8, intent(in)        :: progress_init
    
      progress_bar(1) = 0
      progress_bar(2) = max
      progress_title = title
      progress_active = .True.
      progress_value = progress_init
      call run_progress()
    end
    
    • max:循环次数的最大值
    • title:您监控的标题
    • progress_initprogress_value
    • 的初始值

    终止监控:

    subroutine stop_progress()
      use progress
      implicit none
      progress_active = .False.
      call write_progress()
    end
    
  3. 在进度文件中打印数据的子程序

    subroutine write_progress()
      use progress
      implicit none
      open(unit=progress_unit,file=progress_filename)
      write(progress_unit,'(A)') progress_title
      write(progress_unit,'(F5.1,X,A)') dble(100*progress_bar(1))/dble(progress_bar(2)), '%'
      write(progress_unit,*) progress_value
      close(unit=progress_unit)
    end
    
  4. 使用ALARM信号的核心

    recursive subroutine run_progress()
      use progress
      implicit none
      call write_progress()
      if (progress_active) then
        call alarm(progress_refresh,run_progress)
      endif
    end
    
  5. 这是您使用它的方式:

    program test
      use progress
      implicit none
    
      integer :: i
      integer, parameter :: nmax = 10
    
      double precision :: s
    
    ! --> Before the loop, run start_progress
      s = 0.d0
      call start_progress(nmax, "Dummy loop", s)
      do i=1,nmax
        call sleep(1)
        s = s+1.d0
    
    ! --> Update the global variables at the end of each loop cycle
    !     (there is no function call, so it is almost free)
    
        progress_value  = s
        progress_bar(1) = i
      enddo
    
    ! --> After the loop, call stop_progress
      call stop_progress()
    end
    
  6. 如果您不想使用文件而是使用命名管道(在编写例程中没有open / close语句), 由于其缓冲输出,它可能不适用于Fortran。你可以尝试

        export GFORTRAN_UNBUFFERED_ALL=1
    
    在你的shell中

    来改变它。

    您可以在此处获取代码:https://gist.github.com/scemama/9977dc1290685b541f9c