Files
Fileinterface_Cron_Job/FileinterfaceCronScheduler/IMSApi/ImsApiClient.cs
2026-08-19 19:27:41 +02:00

230 lines
9.2 KiB
C#

using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using FileInterface.Scheduler;
using com.itac.mes.imsapi.client.dotnet;
using com.itac.mes.imsapi.domain.container;
namespace FileinterfaceCronScheduler.IMSApi
{
/// <summary>
/// Initializes IMSApiDotNet, logs in once and exposes customFunction to the scheduler.
/// Configuration is read from App.config.
/// </summary>
public sealed class ImsApiClient : ICustomFunctionInvoker, IDisposable
{
private readonly object _sync = new object();
private readonly object _callSync = new object();
private IIMSApiDotNet _imsapi;
private IMSApiSessionContextStruct _sessionContext;
private bool _initialized;
private bool _disposed;
private readonly bool _serializeCalls = String.Equals(ConfigurationManager.AppSettings["IMS.SerializeCalls"], "true", StringComparison.OrdinalIgnoreCase);
public bool IsInitialized { get { lock (_sync) return _initialized; } }
public bool IsLoggedIn { get { lock (_sync) return _sessionContext != null; } }
public long SessionId { get { lock (_sync) return _sessionContext == null ? 0L : _sessionContext.sessionId; } }
public void Connect()
{
lock (_sync)
{
ThrowIfDisposed();
EnsureInitializedNoLock();
EnsureLoggedInNoLock();
}
}
private void EnsureInitializedNoLock()
{
if (_initialized && _imsapi != null)
return;
string appId = Required("IMS.AppID", "AppID");
string serverUrl = Required("IMS.ServerUrl", "Server_URL");
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
string propDir = Get("IMS.PropertyDirectory", baseDir);
if (!Path.IsPathRooted(propDir))
propDir = Path.Combine(baseDir, propDir);
Directory.CreateDirectory(propDir);
string ihas = Path.Combine(propDir, "ihas.properties");
if (!File.Exists(ihas))
File.WriteAllText(ihas, String.Empty);
Trace.TraceInformation("Initializing IMSApiDotNet. AppID={0}, Server={1}, PropDir={2}", appId, serverUrl, propDir);
IMSApiDotNet.setProperty("itac.appid", appId);
IMSApiDotNet.setProperty("itac.artes.clusternodes", serverUrl);
IMSApiDotNet.setProperty("itac.propdir", propDir);
_imsapi = IMSApiDotNet.loadLibrary();
if (_imsapi == null)
throw new InvalidOperationException("IMSApiDotNet.loadLibrary() returned null.");
string version;
int versionResult = _imsapi.imsapiGetLibraryVersion(out version);
if (versionResult == IMSApiDotNetConstants.RES_OK)
Trace.TraceInformation("IMSApiDotNet library version: " + version);
else
Trace.TraceWarning("imsapiGetLibraryVersion returned " + versionResult);
int initResult = _imsapi.imsapiInit();
if (initResult != IMSApiDotNetConstants.RES_OK)
throw new InvalidOperationException("imsapiInit failed. Result=" + initResult);
_initialized = true;
Trace.TraceInformation("IMSApiDotNet initialized successfully.");
}
private void EnsureLoggedInNoLock()
{
if (_sessionContext != null)
return;
string station = Required("IMS.StationNumber", "StationNr");
// Original FileInterface contains the historic typo 'CleintNO'. Both names are supported.
string client = FirstNonEmpty(
ConfigurationManager.AppSettings["IMS.ClientNumber"],
ConfigurationManager.AppSettings["ClientNO"],
ConfigurationManager.AppSettings["CleintNO"]);
if (String.IsNullOrWhiteSpace(client))
throw new ConfigurationErrorsException("Missing IMS client number. Configure IMS.ClientNumber (or legacy CleintNO).");
string registrationType = Get("IMS.RegistrationType", "S");
var validation = new IMSApiSessionValidationStruct
{
stationNumber = station,
client = client,
registrationType = registrationType
};
Trace.TraceInformation("Calling regLogin(). Station={0}, Client={1}, RegistrationType={2}", station, client, registrationType);
IMSApiSessionContextStruct newSessionContext;
int loginResult = _imsapi.regLogin(validation, out newSessionContext);
if (loginResult != IMSApiDotNetConstants.RES_OK || newSessionContext == null)
throw new InvalidOperationException("IMS regLogin failed. Result=" + loginResult);
_sessionContext = newSessionContext;
Trace.TraceInformation("IMS login successful. SessionId=" + _sessionContext.sessionId);
}
public CustomFunctionCallResult Invoke(string customFunctionName, string[] inArgs)
{
if (String.IsNullOrWhiteSpace(customFunctionName))
return CustomFunctionCallResult.Fail("CustomFunction is empty.");
try
{
IIMSApiDotNet api;
IMSApiSessionContextStruct session;
// Only connection/login state is protected. The actual CustomFunction call
// can run concurrently for independent cron jobs.
lock (_sync)
{
ThrowIfDisposed();
EnsureInitializedNoLock();
EnsureLoggedInNoLock();
api = _imsapi;
session = _sessionContext;
}
if (_serializeCalls)
{
lock (_callSync)
return InvokeCore(api, session, customFunctionName, inArgs);
}
return InvokeCore(api, session, customFunctionName, inArgs);
}
catch (Exception ex)
{
Trace.TraceError("IMS customFunction failed: " + ex);
return CustomFunctionCallResult.Fail(ex.ToString());
}
}
private static CustomFunctionCallResult InvokeCore(
IIMSApiDotNet api,
IMSApiSessionContextStruct session,
string customFunctionName,
string[] inArgs)
{
string[] outArgs;
string customErrorString;
int result = api.customFunction(
session,
customFunctionName,
inArgs ?? new string[0],
out outArgs,
out customErrorString);
string outText = outArgs == null || outArgs.Length == 0
? String.Empty
: String.Join(" | ", outArgs);
if (result != IMSApiDotNetConstants.RES_OK)
{
return CustomFunctionCallResult.Fail(
"IMS result=" + result +
(String.IsNullOrWhiteSpace(customErrorString) ? String.Empty : "; Error=" + customErrorString) +
(String.IsNullOrWhiteSpace(outText) ? String.Empty : "; OutArgs=" + outText));
}
return CustomFunctionCallResult.Ok(
"IMS result=0" +
(String.IsNullOrWhiteSpace(customErrorString) ? String.Empty : "; Info=" + customErrorString) +
(String.IsNullOrWhiteSpace(outText) ? String.Empty : "; OutArgs=" + outText));
}
public void Dispose()
{
lock (_sync)
{
if (_disposed) return;
_disposed = true;
// The FileInterface source currently has no regLogout/imsapiFinish call.
// We therefore release managed references here. If the deployed IMSAPI
// version exposes an explicit logout method, it can safely be added here.
_sessionContext = null;
_imsapi = null;
_initialized = false;
}
}
private static string Get(string key, string fallback)
{
string value = ConfigurationManager.AppSettings[key];
return String.IsNullOrWhiteSpace(value) ? fallback : value.Trim();
}
private static string Required(string preferredKey, string legacyKey)
{
string value = FirstNonEmpty(
ConfigurationManager.AppSettings[preferredKey],
ConfigurationManager.AppSettings[legacyKey]);
if (String.IsNullOrWhiteSpace(value))
throw new ConfigurationErrorsException("Missing setting '" + preferredKey + "' (legacy: '" + legacyKey + "').");
return value.Trim();
}
private static string FirstNonEmpty(params string[] values)
{
if (values == null) return null;
foreach (string value in values)
if (!String.IsNullOrWhiteSpace(value)) return value.Trim();
return null;
}
private void ThrowIfDisposed()
{
if (_disposed) throw new ObjectDisposedException(GetType().FullName);
}
}
}