From 0389e4002fcf556c5e48dd65efead815db314f61 Mon Sep 17 00:00:00 2001
From: misaki <1018407731@qq.com>
Date: Fri, 18 Apr 2025 11:00:47 +0800
Subject: [PATCH] 3
---
1.xlsx | Bin 0 -> 6861 bytes
Controllers/FortranController.cs | 74 ++++
Dockerfile | 88 +++++
Fortran/SUB_UNITHYDRO.f90 | 113 ++++++
FortranWebApi.csproj | 27 ++
HttpRequests/fortranwebapi.http | 22 ++
Models/ApiResponse.cs | 31 ++
Models/FortranParameter.cs | 22 ++
Models/FortranRequest.cs | 13 +
Models/FortranRequestWrapper.cs | 7 +
Program.cs | 80 +++++
Properties/launchSettings.json | 14 +
Services/FortranInteropService.cs | 562 ++++++++++++++++++++++++++++++
appsettings.json | 16 +
14 files changed, 1069 insertions(+)
create mode 100644 1.xlsx
create mode 100644 Controllers/FortranController.cs
create mode 100644 Dockerfile
create mode 100644 Fortran/SUB_UNITHYDRO.f90
create mode 100644 FortranWebApi.csproj
create mode 100644 HttpRequests/fortranwebapi.http
create mode 100644 Models/ApiResponse.cs
create mode 100644 Models/FortranParameter.cs
create mode 100644 Models/FortranRequest.cs
create mode 100644 Models/FortranRequestWrapper.cs
create mode 100644 Program.cs
create mode 100644 Properties/launchSettings.json
create mode 100644 Services/FortranInteropService.cs
create mode 100644 appsettings.json
diff --git a/1.xlsx b/1.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..1b8aebfcf39465a7c4ab5d5f24d98f7b11ac1294
GIT binary patch
literal 6861
zcmcIp2Ut^Evkd~G5fCv5DAGhgS{j7ji&CW{ASEQBlRy%BQIU>R6{#XsPyvx9C?Z8f
zMMR49-a8^q>I-=9<;uJN`(F89@|}|}=j>T~vi6$UXC~U}
fjwXX>
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
literal 0
HcmV?d00001
diff --git a/Controllers/FortranController.cs b/Controllers/FortranController.cs
new file mode 100644
index 0000000..46a63e2
--- /dev/null
+++ b/Controllers/FortranController.cs
@@ -0,0 +1,74 @@
+using Microsoft.AspNetCore.Mvc;
+using FortranWebApi.Models;
+using FortranWebApi.Services;
+using System.Text.Json;
+
+namespace FortranWebApi.Controllers
+{
+ [ApiController]
+ [Route("[controller]")]
+ public class FortranCalculateController : ControllerBase
+ {
+ private readonly FortranInteropService _fortranService;
+ private readonly ILogger _logger;
+
+ public FortranCalculateController(FortranInteropService fortranService, ILogger logger)
+ {
+ _fortranService = fortranService;
+ _logger = logger;
+ }
+
+ [HttpPost]
+ public IActionResult Post([FromBody] FortranRequestWrapper wrapper)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(wrapper.Text))
+ {
+ return BadRequest(new
+ {
+ message = "请求文本不能为空",
+ success = false,
+ data = (object)null
+ });
+ }
+
+ string result = _fortranService.ProcessFortranRequest(wrapper.Text);
+
+ // 使用驼峰命名法解析结果
+ var options = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
+ PropertyNameCaseInsensitive = true,
+ WriteIndented = true
+ };
+
+ var resultObj = JsonSerializer.Deserialize(result, options);
+
+ // 返回新的格式
+ var response = new ApiResponse
+ {
+ Message = "Success",
+ Success = true,
+ Data = new ApiResponseData
+ {
+ Value = resultObj
+ }
+ };
+
+ return Ok(response);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "处理Fortran请求时发生错误");
+ return StatusCode(500, new
+ {
+ message = ex.Message,
+ success = false,
+ data = (object)null
+ });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..807f2d9
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,88 @@
+# ===== 第一阶段:构建阶段 =====
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+
+# 配置 NuGet 使用国内镜像源
+RUN dotnet nuget add source https://mirrors.cloud.tencent.com/nuget/ \
+ && dotnet nuget disable source nuget.org
+
+# 配置 apt-get 使用 apt-cacher-ng 作为代理
+RUN echo 'Acquire::http::Proxy "http://192.168.1.140:3142";' > /etc/apt/apt.conf.d/01proxy
+
+# 创建并配置 Debian 镜像源
+RUN echo "deb https://mirrors.ustc.edu.cn/debian/ bookworm main contrib non-free non-free-firmware" > /etc/apt/sources.list && \
+ echo "deb https://mirrors.ustc.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
+ echo "deb https://mirrors.ustc.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list
+
+# 允许使用不安全的软件源
+RUN echo 'Acquire::AllowInsecureRepositories "true";' > /etc/apt/apt.conf.d/99allow-insecure && \
+ echo 'Acquire::AllowDowngradeToInsecureRepositories "true";' >> /etc/apt/apt.conf.d/99allow-insecure
+
+WORKDIR /src
+
+# 安装 Fortran 编译器
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ gfortran \
+ && rm -rf /var/lib/apt/lists/*
+
+# 复制 Fortran 源文件并编译
+COPY Fortran/*.f90 /src/Fortran/
+WORKDIR /src/Fortran
+
+# 编译所有 .f90 文件为 .o 文件
+RUN for file in *.f90; do gfortran -fPIC -c "$file" -o "${file%.f90}.o"; done
+
+# 链接所有 .o 文件为共享库
+RUN gfortran -shared *.o -o libSUB_UNITHYDRO.so
+
+# 回到主工作目录
+WORKDIR /src
+
+# 只复制项目文件
+COPY ["FortranWebApi.csproj", "./"]
+RUN dotnet restore
+
+# 复制源代码
+COPY . .
+
+# 合并 build 和 publish 命令
+RUN dotnet publish -c Release -o /app/publish
+
+# 复制编译好的 .so 文件到发布目录
+RUN mkdir -p /app/publish && \
+ cp /src/Fortran/libSUB_UNITHYDRO.so /app/publish/
+
+# ===== 第二阶段:运行阶段 =====
+FROM mcr.microsoft.com/dotnet/aspnet:8.0
+
+# 配置 apt-get 使用 apt-cacher-ng 作为代理
+RUN echo 'Acquire::http::Proxy "http://192.168.1.140:3142";' > /etc/apt/apt.conf.d/01proxy
+
+# 创建并配置 Debian 镜像源
+RUN echo "deb https://mirrors.ustc.edu.cn/debian/ bookworm main contrib non-free non-free-firmware" > /etc/apt/sources.list && \
+ echo "deb https://mirrors.ustc.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
+ echo "deb https://mirrors.ustc.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list
+
+# 允许使用不安全的软件源
+RUN echo 'Acquire::AllowInsecureRepositories "true";' > /etc/apt/apt.conf.d/99allow-insecure && \
+ echo 'Acquire::AllowDowngradeToInsecureRepositories "true";' >> /etc/apt/apt.conf.d/99allow-insecure
+
+# 安装运行时依赖
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ libc6-dev \
+ libgfortran5 \
+ && rm -rf /var/lib/apt/lists/*
+
+WORKDIR /app
+COPY --from=build /app/publish .
+
+# 确保 .so 文件的权限正确
+RUN chmod 755 /app/libSUB_UNITHYDRO.so
+
+# 设置 LD_LIBRARY_PATH
+ENV LD_LIBRARY_PATH=/app
+
+# 设置端口和监听地址
+ENV ASPNETCORE_URLS=http://+:5000
+EXPOSE 5000
+
+ENTRYPOINT ["dotnet", "FortranWebApi.dll"]
\ No newline at end of file
diff --git a/Fortran/SUB_UNITHYDRO.f90 b/Fortran/SUB_UNITHYDRO.f90
new file mode 100644
index 0000000..c7b805e
--- /dev/null
+++ b/Fortran/SUB_UNITHYDRO.f90
@@ -0,0 +1,113 @@
+
+ !鵥λߺʵĽ̺ͺˮ
+
+ SUBROUTINE SUB_UNITHYDRO(S,& !
+ QT,& !̽
+ M,& !ʱ
+ Q,& !͵λ
+ N,& !λʱ
+ IM,& !ˮ
+ FC,& !ȶ
+ QF,& !
+ T,& !ÿʱεʱ
+ A,& !澶ʱıֵ
+ RUNOFF_U,& !澶
+ RUNOFF_G,& !¾
+ RUNOFF)& !ܵĺˮ
+ bind(C, name="SUB_UNITHYDRO")
+ !DEC$ ATTRIBUTES DLLEXPORT::SUB_UNITHYDRO
+ integer :: m,n
+ real :: S,Qt(m),q(n),Im,fc,Qf,t,a
+ real :: Qsum(m),Qj_sum(m),Qj_t(m),Qj_g(m),Qj_u(m)
+ real :: runoff_ut(100,100),runoff_u(100),runoff_g(100),W(100)
+ integer :: x,y
+ real :: Wg,Tg,Qmg
+ real :: Pa !ǰӰ
+ real :: Qj_eu(100) !ѵ澻0ĵ
+ real :: Qj_gs=0 !¾
+ integer :: i,j !
+
+ !************** 㼰ķָ **************
+
+ !ۼƽ
+ Qsum(1)=Qt(1)
+ do i=2,m
+ Qsum(i)=Qsum(i-1)+Qt(i)
+ end do
+
+ !
+ Pa=Im*2/3
+ do i=1,m
+ if(Qsum(i)<=(Im-Pa)) then
+ Qj_t(i)=0
+ Qj_g(i)=0
+ else
+ Qj_t(i)=Qsum(i)-(Im-Pa)
+ Qj_g(i)=(Qj_t(i)/Qt(i))*t*fc
+ exit
+ end if
+ end do
+ do while(iQt(i+1)) Qj_g(i+1)=Qt(i+1) !¾
+ i=i+1
+ end do
+ Qj_sum(1)=Qj_t(1)
+ Qj_u(1)=Qj_t(1)-Qj_g(1)
+ do i=2,m
+ Qj_sum(i)=Qj_sum(i-1)+Qj_t(i) !ۼƾ
+ Qj_u(i)=Qj_t(i)-Qj_g(i) !澻
+ end do
+
+ !******************** ˮ **************************
+
+ !澶*************************************
+
+ x=0
+ do i=1,m
+ if(Qj_u(i)/=0) then
+ x=x+1
+ Qj_eu(x)=Qj_u(i)
+ end if
+ end do
+ runoff_ut=0
+ runoff_u=0
+ y=n+x
+
+ !λ߷ʱε澶
+ do i=1,n
+ do j=1,x
+ runoff_ut(j,i+j-1)=q(i)*Qj_eu(j)/10.0
+ end do
+ end do
+
+ !澶
+ do i=1,y
+ do j=1,x
+ runoff_u(i)=runoff_u(i)+runoff_ut(j,i)
+ end do
+ end do
+
+ !¾*********************************
+ runoff_g(1)=0
+ do i=1,m
+ Qj_gs=Qj_gs+Qj_g(i)
+ end do
+ Wg=1000*Qj_gs*S
+ Tg=a*(y-2)*t
+ Qmg=2*Wg/(Tg*3600)
+
+ do i=2,y
+ runoff_g(i)=runoff_g(i-1)+Qmg/(a*(y-2)/2)
+ if(i>(a*(y-2)/2+1)) runoff_g(i)=runoff_g(i-1)-Qmg/(a*(y-2)/2)
+ end do
+
+ !ܵĺˮ*********************************
+ do i=1,y
+ W(i)=runoff_u(i)+runoff_g(i)+Qf
+ end do
+
+ end subroutine
+
+
diff --git a/FortranWebApi.csproj b/FortranWebApi.csproj
new file mode 100644
index 0000000..6a4d551
--- /dev/null
+++ b/FortranWebApi.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net8.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
\ No newline at end of file
diff --git a/HttpRequests/fortranwebapi.http b/HttpRequests/fortranwebapi.http
new file mode 100644
index 0000000..3577c23
--- /dev/null
+++ b/HttpRequests/fortranwebapi.http
@@ -0,0 +1,22 @@
+@FortranWebApi_HostAddress = http://localhost:5000
+
+### 测试Fortran请求
+
+POST {{FortranWebApi_HostAddress}}/FortranCalculate
+Content-Type: application/json
+
+{
+ "text": "{\"FuncName\":\"calculate_main\",\"ClassName\":\"\",\"Par\":[ {"Name":"S","DataType":"1","ArrayType":"0","IsOut":"2","Data":0},
+ {"Name":"QT","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]},
+ {"Name":"M","DataType":"0","ArrayType":"0","IsOut":"2","Data":0},
+ {"Name":"Q","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]},
+ {"Name":"N","DataType":"0","ArrayType":"0","IsOut":"2","Data":0},
+ {"Name":"IM","DataType":"1","ArrayType":"0","IsOut":"2","Data":0},
+ {"Name":"FC","DataType":"1","ArrayType":"0","IsOut":"2","Data":0},
+ {"Name":"QF","DataType":"1","ArrayType":"0","IsOut":"2","Data":0},
+ {"Name":"T","DataType":"1","ArrayType":"0","IsOut":"2","Data":0},
+ {"Name":"A","DataType":"1","ArrayType":"0","IsOut":"2","Data":0},
+ {"Name":"RUNOFF_U","DataType":"1","ArrayType":"1","IsOut":"1","Data":[]},
+ {"Name":"RUNOFF_G","DataType":"1","ArrayType":"1","IsOut":"1","Data":[]},
+ {"Name":"RUNOFF","DataType":"1","ArrayType":"1","IsOut":"1","Data":[]}]}"
+}
\ No newline at end of file
diff --git a/Models/ApiResponse.cs b/Models/ApiResponse.cs
new file mode 100644
index 0000000..3845aa5
--- /dev/null
+++ b/Models/ApiResponse.cs
@@ -0,0 +1,31 @@
+using System.Text.Json.Serialization;
+
+namespace FortranWebApi.Models
+{
+ public class ApiResponse
+ {
+ [JsonPropertyName("message")]
+ public string Message { get; set; } = "Success";
+
+ [JsonPropertyName("success")]
+ public bool Success { get; set; } = true;
+
+ [JsonPropertyName("data")]
+ public ApiResponseData? Data { get; set; }
+ }
+
+ public class ApiResponseData
+ {
+ [JsonPropertyName("contentType")]
+ public string? ContentType { get; set; }
+
+ [JsonPropertyName("serializerSettings")]
+ public object? SerializerSettings { get; set; }
+
+ [JsonPropertyName("statusCode")]
+ public int? StatusCode { get; set; }
+
+ [JsonPropertyName("value")]
+ public T? Value { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Models/FortranParameter.cs b/Models/FortranParameter.cs
new file mode 100644
index 0000000..78d68f0
--- /dev/null
+++ b/Models/FortranParameter.cs
@@ -0,0 +1,22 @@
+using System.Text.Json.Serialization;
+
+namespace FortranWebApi.Models
+{
+ public class FortranParameter
+ {
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("dataType")]
+ public string DataType { get; set; } = "0";
+
+ [JsonPropertyName("arrayType")]
+ public string ArrayType { get; set; } = "0";
+
+ [JsonPropertyName("isOut")]
+ public string IsOut { get; set; } = "2";
+
+ [JsonPropertyName("data")]
+ public object? Data { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Models/FortranRequest.cs b/Models/FortranRequest.cs
new file mode 100644
index 0000000..e17fc88
--- /dev/null
+++ b/Models/FortranRequest.cs
@@ -0,0 +1,13 @@
+using System.Text.Json.Serialization;
+
+namespace FortranWebApi.Models
+{
+ public class FortranRequest
+ {
+ public string FuncName { get; set; } = string.Empty;
+ public string ClassName { get; set; } = string.Empty;
+
+ [JsonPropertyName("par")]
+ public List Parameters { get; set; } = new List();
+ }
+}
\ No newline at end of file
diff --git a/Models/FortranRequestWrapper.cs b/Models/FortranRequestWrapper.cs
new file mode 100644
index 0000000..49c36c2
--- /dev/null
+++ b/Models/FortranRequestWrapper.cs
@@ -0,0 +1,7 @@
+namespace FortranWebApi.Models
+{
+ public class FortranRequestWrapper
+ {
+ public string Text { get; set; } = string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
new file mode 100644
index 0000000..ac334f1
--- /dev/null
+++ b/Program.cs
@@ -0,0 +1,80 @@
+using Serilog;
+using FortranWebApi.Services;
+using System.IO;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+// 设置库搜索路径
+string currentDir = Directory.GetCurrentDirectory();
+Environment.SetEnvironmentVariable("LD_LIBRARY_PATH",
+ $"{Environment.GetEnvironmentVariable("LD_LIBRARY_PATH")}:{currentDir}");
+
+var builder = WebApplication.CreateBuilder(args);
+
+// 添加健康检查服务
+builder.Services.AddHealthChecks();
+
+// 配置Serilog
+Log.Logger = new LoggerConfiguration()
+ .WriteTo.Console(outputTemplate:
+ "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
+ .WriteTo.File("logs/log-.txt",
+ rollingInterval: RollingInterval.Day,
+ outputTemplate:
+ "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
+ .CreateLogger();
+
+builder.Host.UseSerilog(); // 将Serilog添加到Host
+
+// 配置JSON序列化选项,使用驼峰命名法
+builder.Services.AddControllers().AddJsonOptions(options =>
+{
+ options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
+ options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
+ options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
+ options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
+});
+
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+builder.Services.AddSingleton();
+
+// 配置CORS
+builder.Services.AddCors(options =>
+{
+ options.AddDefaultPolicy(policy =>
+ {
+ policy.AllowAnyOrigin()
+ .AllowAnyMethod()
+ .AllowAnyHeader();
+ });
+});
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseCors();
+app.UseAuthorization();
+// 映射健康检查端点
+app.MapHealthChecks("/health");
+app.MapControllers();
+
+try
+{
+ Log.Information("启动应用程序...");
+ app.Run();
+}
+catch (Exception ex)
+{
+ Log.Fatal(ex, "应用程序启动失败");
+}
+finally
+{
+ Log.CloseAndFlush();
+}
\ No newline at end of file
diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json
new file mode 100644
index 0000000..295de27
--- /dev/null
+++ b/Properties/launchSettings.json
@@ -0,0 +1,14 @@
+{
+ "profiles": {
+ "FortranWebApi": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "dotnetRunMessages": true,
+ "applicationUrl": "http://0.0.0.0:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Services/FortranInteropService.cs b/Services/FortranInteropService.cs
new file mode 100644
index 0000000..e256640
--- /dev/null
+++ b/Services/FortranInteropService.cs
@@ -0,0 +1,562 @@
+using System.Runtime.InteropServices;
+using FortranWebApi.Models;
+using System.Text.Json;
+using System.Runtime.InteropServices.Marshalling;
+using System.IO;
+
+namespace FortranWebApi.Services
+{
+ public class FortranInteropService
+ {
+ // 静态构造函数设置DLL导入解析器
+ static FortranInteropService()
+ {
+ // 添加当前目录到库搜索路径
+ NativeLibrary.SetDllImportResolver(typeof(FortranInteropService).Assembly, (name, assembly, path) =>
+ {
+ if (name == "libSUB_UNITHYDRO.so")
+ {
+ // 尝试从当前目录加载
+ string currentDir = Directory.GetCurrentDirectory();
+ string libraryPath = Path.Combine(currentDir, "libSUB_UNITHYDRO.so");
+
+ if (File.Exists(libraryPath))
+ {
+ return NativeLibrary.Load(libraryPath);
+ }
+ }
+
+ // 回退到默认加载行为
+ return IntPtr.Zero;
+ });
+ }
+
+ // DllImport声明
+ [DllImport("libSUB_UNITHYDRO.so", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SUB_UNITHYDRO")]
+ private static extern void SUB_UNITHYDRO(
+ ref float S,
+ float[] QT,
+ ref int M,
+ float[] Q,
+ ref int N,
+ ref float IM,
+ ref float FC,
+ ref float QF,
+ ref float T,
+ ref float A,
+ float[] RUNOFF_U,
+ float[] RUNOFF_G,
+ float[] RUNOFF
+ );
+
+ public string ProcessFortranRequest(string requestText)
+ {
+ try
+ {
+ // 解析请求
+ var options = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
+ PropertyNameCaseInsensitive = true,
+ WriteIndented = true
+ };
+
+ var request = JsonSerializer.Deserialize(requestText, options);
+ if (request == null)
+ {
+ throw new ArgumentException("无效的请求格式");
+ }
+
+ // 准备输入参数
+ var parameters = request.Parameters;
+
+ // 提取参数
+ float S = GetFloatParameter(parameters, "S");
+ float[] QT = GetFloatArrayParameter(parameters, "QT");
+ int M = GetIntParameter(parameters, "M");
+ float[] Q = GetFloatArrayParameter(parameters, "Q");
+ int N = GetIntParameter(parameters, "N");
+ float IM = GetFloatParameter(parameters, "IM");
+ float FC = GetFloatParameter(parameters, "FC");
+ float QF = GetFloatParameter(parameters, "QF");
+ float T = GetFloatParameter(parameters, "T");
+ float A = GetFloatParameter(parameters, "A");
+
+
+ // 准备数组参数
+ float[] RUNOFF_U = new float[10000]; // 输出数组,初始大小为10000
+ float[] RUNOFF_G = new float[10000]; // 输出数组,初始大小为10000
+ float[] RUNOFF = new float[10000]; // 输出数组,初始大小为10000
+
+
+ // 调用Fortran函数
+ SUB_UNITHYDRO(
+ ref S,
+ QT,
+ ref M,
+ Q,
+ ref N,
+ ref IM,
+ ref FC,
+ ref QF,
+ ref T,
+ ref A,
+ RUNOFF_U,
+ RUNOFF_G,
+ RUNOFF
+ );
+
+ // 更新输出参数
+ UpdateArrayParameter(parameters, "RUNOFF_U", RUNOFF_U);
+ UpdateArrayParameter(parameters, "RUNOFF_G", RUNOFF_G);
+ UpdateArrayParameter(parameters, "RUNOFF", RUNOFF);
+
+
+ // 处理输出数组
+ // 处理输出数组 RUNOFF_U
+ // 注意:没有找到明确的长度参数,使用非零元素数量
+ {
+ int nonZeroCount = 0;
+ for (int i = 0; i < RUNOFF_U.Length; i++)
+ {
+ if (RUNOFF_U[i] != 0) nonZeroCount = i + 1;
+ }
+ if (nonZeroCount > 0)
+ {
+ float[] resultArray = new float[nonZeroCount];
+ Array.Copy(RUNOFF_U, resultArray, nonZeroCount);
+ UpdateArrayParameter(parameters, "RUNOFF_U", resultArray);
+ }
+ }
+ // 处理输出数组 RUNOFF_G
+ // 注意:没有找到明确的长度参数,使用非零元素数量
+ {
+ int nonZeroCount = 0;
+ for (int i = 0; i < RUNOFF_G.Length; i++)
+ {
+ if (RUNOFF_G[i] != 0) nonZeroCount = i + 1;
+ }
+ if (nonZeroCount > 0)
+ {
+ float[] resultArray = new float[nonZeroCount];
+ Array.Copy(RUNOFF_G, resultArray, nonZeroCount);
+ UpdateArrayParameter(parameters, "RUNOFF_G", resultArray);
+ }
+ }
+ // 处理输出数组 RUNOFF
+ // 注意:没有找到明确的长度参数,使用非零元素数量
+ {
+ int nonZeroCount = 0;
+ for (int i = 0; i < RUNOFF.Length; i++)
+ {
+ if (RUNOFF[i] != 0) nonZeroCount = i + 1;
+ }
+ if (nonZeroCount > 0)
+ {
+ float[] resultArray = new float[nonZeroCount];
+ Array.Copy(RUNOFF, resultArray, nonZeroCount);
+ UpdateArrayParameter(parameters, "RUNOFF", resultArray);
+ }
+ }
+
+
+ // 返回结果 - 只返回原始请求的结构,但包含更新后的参数
+ var result = new FortranRequest
+ {
+ FuncName = request.FuncName,
+ ClassName = request.ClassName,
+ Parameters = parameters
+ };
+
+ return JsonSerializer.Serialize(result, options);
+ }
+ catch (Exception ex)
+ {
+ return JsonSerializer.Serialize(new { error = ex.Message });
+ }
+ }
+
+ private float GetFloatParameter(List parameters, string name)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param == null || param.Data == null)
+ {
+ return 0.0f;
+ }
+
+ if (param.Data is JsonElement element)
+ {
+ if (element.ValueKind == JsonValueKind.Number)
+ {
+ return element.GetSingle();
+ }
+ }
+
+ return Convert.ToSingle(param.Data);
+ }
+ private double GetDoubleParameter(List parameters, string name)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param == null || param.Data == null)
+ {
+ return 0.0;
+ }
+
+ if (param.Data is JsonElement element)
+ {
+ if (element.ValueKind == JsonValueKind.Number)
+ {
+ return element.GetSingle();
+ }
+ }
+
+ return Convert.ToDouble(param.Data);
+ }
+ private int GetIntParameter(List parameters, string name)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param == null || param.Data == null)
+ {
+ return 0;
+ }
+
+ if (param.Data is JsonElement element)
+ {
+ if (element.ValueKind == JsonValueKind.Number)
+ {
+ return element.GetInt32();
+ }
+ }
+
+ return Convert.ToInt32(param.Data);
+ }
+ private int[] GetIntArrayParameter(List parameters, string name)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param == null || param.Data == null)
+ {
+ return Array.Empty();
+ }
+
+ if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
+ {
+ var array = new List();
+ foreach (var item in element.EnumerateArray())
+ {
+ if (item.ValueKind == JsonValueKind.Number)
+ {
+ array.Add(item.GetInt32());
+ }
+ }
+ return array.ToArray();
+ }
+
+ if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
+ {
+ var array = new List();
+ foreach (var item in enumerable)
+ {
+ array.Add(Convert.ToInt32(item));
+ }
+ return array.ToArray();
+ }
+
+ return Array.Empty();
+ }
+ private float[] GetFloatArrayParameter(List parameters, string name)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param == null || param.Data == null)
+ {
+ return Array.Empty();
+ }
+
+ if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
+ {
+ var array = new List();
+ foreach (var item in element.EnumerateArray())
+ {
+ if (item.ValueKind == JsonValueKind.Number)
+ {
+ array.Add(item.GetSingle());
+ }
+ }
+ return array.ToArray();
+ }
+
+ if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
+ {
+ var array = new List();
+ foreach (var item in enumerable)
+ {
+ array.Add(Convert.ToSingle(item));
+ }
+ return array.ToArray();
+ }
+
+ return Array.Empty();
+ }
+ private double[] GetDoubleArrayParameter(List parameters, string name)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param == null || param.Data == null)
+ {
+ return Array.Empty();
+ }
+
+ if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
+ {
+ var array = new List();
+ foreach (var item in element.EnumerateArray())
+ {
+ if (item.ValueKind == JsonValueKind.Number)
+ {
+ array.Add(item.GetSingle());
+ }
+ }
+ return array.ToArray();
+ }
+
+ if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
+ {
+ var array = new List();
+ foreach (var item in enumerable)
+ {
+ array.Add(Convert.ToSingle(item));
+ }
+ return array.ToArray();
+ }
+
+ return Array.Empty();
+ }
+ private void UpdateParameter(List parameters, string name, object value)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param != null)
+ {
+ param.Data = value;
+ }
+ }
+
+ private void UpdateArrayParameter(List parameters, string name, float[] value)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param != null)
+ {
+ param.Data = value;
+ }
+ }
+ private void UpdateDoubleArrayParameter(List parameters, string name, double[] value)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param != null)
+ {
+ param.Data = value;
+ }
+ }
+ private void UpdateIntArrayParameter(List parameters, string name, int[] value)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param != null)
+ {
+ param.Data = value;
+ }
+ }
+ // 添加新的辅助方法来处理二维数组
+ private double[,] GetDouble2DArrayParameter(List parameters, string name, int rows, int cols)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param == null || param.Data == null)
+ {
+ return new double[rows, cols];
+ }
+
+ double[,] result = new double[rows, cols];
+
+ if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
+ {
+ int index = 0;
+ foreach (var item in element.EnumerateArray())
+ {
+ if (item.ValueKind == JsonValueKind.Number)
+ {
+ int row = index / cols;
+ int col = index % cols;
+ if (row < rows && col < cols)
+ {
+ result[row, col] = item.GetDouble();
+ }
+ index++;
+ }
+ }
+ }
+ else if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
+ {
+ int index = 0;
+ foreach (var item in enumerable)
+ {
+ int row = index / cols;
+ int col = index % cols;
+ if (row < rows && col < cols)
+ {
+ result[row, col] = Convert.ToDouble(item);
+ }
+ index++;
+ }
+ }
+
+ return result;
+ }
+
+ // 添加更新二维数组参数的方法
+ private void UpdateDouble2DArrayParameter(List parameters, string name, double[,] value)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param != null)
+ {
+ // 将二维数组转换为一维数组以便于JSON序列化
+ int rows = value.GetLength(0);
+ int cols = value.GetLength(1);
+ double[] flatArray = new double[rows * cols];
+ for (int i = 0; i < rows; i++)
+ {
+ for (int j = 0; j < cols; j++)
+ {
+ flatArray[i * cols + j] = value[i, j];
+ }
+ }
+ param.Data = flatArray;
+ }
+ }
+ // float版本
+ private float[,] GetFloat2DArrayParameter(List parameters, string name, int rows, int cols)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param == null || param.Data == null)
+ {
+ return new float[rows, cols];
+ }
+
+ float[,] result = new float[rows, cols];
+
+ if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
+ {
+ int index = 0;
+ foreach (var item in element.EnumerateArray())
+ {
+ if (item.ValueKind == JsonValueKind.Number)
+ {
+ int row = index / cols;
+ int col = index % cols;
+ if (row < rows && col < cols)
+ {
+ result[row, col] = item.GetSingle();
+ }
+ index++;
+ }
+ }
+ }
+ else if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
+ {
+ int index = 0;
+ foreach (var item in enumerable)
+ {
+ int row = index / cols;
+ int col = index % cols;
+ if (row < rows && col < cols)
+ {
+ result[row, col] = Convert.ToSingle(item);
+ }
+ index++;
+ }
+ }
+
+ return result;
+ }
+
+ private void UpdateFloat2DArrayParameter(List parameters, string name, float[,] value)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param != null)
+ {
+ int rows = value.GetLength(0);
+ int cols = value.GetLength(1);
+ float[] flatArray = new float[rows * cols];
+ for (int i = 0; i < rows; i++)
+ {
+ for (int j = 0; j < cols; j++)
+ {
+ flatArray[i * cols + j] = value[i, j];
+ }
+ }
+ param.Data = flatArray;
+ }
+ }
+
+ // int版本
+ private int[,] GetInt2DArrayParameter(List parameters, string name, int rows, int cols)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param == null || param.Data == null)
+ {
+ return new int[rows, cols];
+ }
+
+ int[,] result = new int[rows, cols];
+
+ if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
+ {
+ int index = 0;
+ foreach (var item in element.EnumerateArray())
+ {
+ if (item.ValueKind == JsonValueKind.Number)
+ {
+ int row = index / cols;
+ int col = index % cols;
+ if (row < rows && col < cols)
+ {
+ result[row, col] = item.GetInt32();
+ }
+ index++;
+ }
+ }
+ }
+ else if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
+ {
+ int index = 0;
+ foreach (var item in enumerable)
+ {
+ int row = index / cols;
+ int col = index % cols;
+ if (row < rows && col < cols)
+ {
+ result[row, col] = Convert.ToInt32(item);
+ }
+ index++;
+ }
+ }
+
+ return result;
+ }
+
+ private void UpdateInt2DArrayParameter(List parameters, string name, int[,] value)
+ {
+ var param = parameters.FirstOrDefault(p => p.Name == name);
+ if (param != null)
+ {
+ int rows = value.GetLength(0);
+ int cols = value.GetLength(1);
+ int[] flatArray = new int[rows * cols];
+ for (int i = 0; i < rows; i++)
+ {
+ for (int j = 0; j < cols; j++)
+ {
+ flatArray[i * cols + j] = value[i, j];
+ }
+ }
+ param.Data = flatArray;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/appsettings.json b/appsettings.json
new file mode 100644
index 0000000..dcb0750
--- /dev/null
+++ b/appsettings.json
@@ -0,0 +1,16 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "Kestrel": {
+ "Endpoints": {
+ "Http": {
+ "Url": "http://0.0.0.0:5000"
+ }
+ }
+ }
+}
\ No newline at end of file