监控可执行文件的进度,以便在网络表单上报告
Monitor progress of executable so it can be reported on webform
我最近一直在考虑采用一个遗留建筑分析程序并为其提供一个带有 asp.NET Webforms 和 javascript 的前端。 (遗留代码是用 Fortran 语言编写的,目的是 运行 在 Web 服务器或另一台远程计算机上。)
问题:是否可以监视可执行文件的进程(运行在 Web 服务器或其他地方),以便可以在 Web 表单上报告?
遗留代码仍然是正在进行的开发计划的一部分。我们不打算将其重新编码为 C# 等,因为:
1) 性能至上;和
2) 这将是一项艰巨的任务。
但是可以修改 Fortran 代码以输出某些内容或调用某些内容来更新进度并让用户知道(希望通过 Webform)可执行文件的运行情况。
有没有人遇到或应用过可以提供此功能的编程模型?
FORTRAN 程序可以将状态(比如开始于 25/02/2015 12:05:57,完成于 25/02/2015 12:08:38)写入文本文件或数据库 table.在状态网页上,您可以使用自动刷新或 jQuery ajax 调用来检查进程的状态并更新它。
我发现在 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)
调用和输入到你的助手子程序。要获取此处使用的辅助函数 makeDirectory
和 findNewFileHandle
,请参阅 the following Fortran 90 module。
如果您习惯于处理文本文件,可能会觉得这些东西很奇怪,但在 Fortran 中,这种方式更安全、更舒适 - 如果您想编写纯文本,您会遇到很多困难,直到您明白没错,我告诉你。
要制作不影响代码性能的进度条之类的东西,您可以这样做。
在模块中创建全局变量
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
: 存储进度的文件名
创建 start/stop 监控的例程
初始化监控:
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_init
: progress_value
的初始值
终止监听:
subroutine stop_progress()
use progress
implicit none
progress_active = .False.
call write_progress()
end
打印进度文件中数据的子程序
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
以及使用ALARM
信号的核心
recursive subroutine run_progress()
use progress
implicit none
call write_progress()
if (progress_active) then
call alarm(progress_refresh,run_progress)
endif
end
你是这样使用它的:
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
如果您不想使用文件而是命名管道(在编写例程中没有 open/close 语句),
由于其缓冲输出,它可能不适用于 Fortran。你可以试试
export GFORTRAN_UNBUFFERED_ALL=1
在您的 shell 中进行更改。
您可以在这里获取代码:https://gist.github.com/scemama/9977dc1290685b541f9c
我更喜欢从 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/
我最近一直在考虑采用一个遗留建筑分析程序并为其提供一个带有 asp.NET Webforms 和 javascript 的前端。 (遗留代码是用 Fortran 语言编写的,目的是 运行 在 Web 服务器或另一台远程计算机上。)
问题:是否可以监视可执行文件的进程(运行在 Web 服务器或其他地方),以便可以在 Web 表单上报告?
遗留代码仍然是正在进行的开发计划的一部分。我们不打算将其重新编码为 C# 等,因为:
1) 性能至上;和
2) 这将是一项艰巨的任务。
但是可以修改 Fortran 代码以输出某些内容或调用某些内容来更新进度并让用户知道(希望通过 Webform)可执行文件的运行情况。
有没有人遇到或应用过可以提供此功能的编程模型?
FORTRAN 程序可以将状态(比如开始于 25/02/2015 12:05:57,完成于 25/02/2015 12:08:38)写入文本文件或数据库 table.在状态网页上,您可以使用自动刷新或 jQuery ajax 调用来检查进程的状态并更新它。
我发现在 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)
调用和输入到你的助手子程序。要获取此处使用的辅助函数 makeDirectory
和 findNewFileHandle
,请参阅 the following Fortran 90 module。
如果您习惯于处理文本文件,可能会觉得这些东西很奇怪,但在 Fortran 中,这种方式更安全、更舒适 - 如果您想编写纯文本,您会遇到很多困难,直到您明白没错,我告诉你。
要制作不影响代码性能的进度条之类的东西,您可以这样做。
在模块中创建全局变量
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
: 存储进度的文件名
创建 start/stop 监控的例程
初始化监控:
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_init
:progress_value
的初始值
终止监听:
subroutine stop_progress() use progress implicit none progress_active = .False. call write_progress() end
打印进度文件中数据的子程序
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
以及使用
ALARM
信号的核心recursive subroutine run_progress() use progress implicit none call write_progress() if (progress_active) then call alarm(progress_refresh,run_progress) endif end
你是这样使用它的:
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
如果您不想使用文件而是命名管道(在编写例程中没有 open/close 语句), 由于其缓冲输出,它可能不适用于 Fortran。你可以试试
export GFORTRAN_UNBUFFERED_ALL=1
在您的 shell 中进行更改。
您可以在这里获取代码:https://gist.github.com/scemama/9977dc1290685b541f9c
我更喜欢从 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/