Merge branch 'release/SimulaAddNumArt01'
This commit is contained in:
@@ -24,6 +24,13 @@ namespace IOB_UT_NEXT
|
||||
/// </summary>
|
||||
public Dictionary<string, string> optKVP { get; set; } = new Dictionary<string, string>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Dizionario opzionale di configurazione memorie WRITE con "alias",
|
||||
/// impiegato tipicamente per poter gestire scritture valori INT su CNC/PLC che non accettano stringhe (es FANUC)
|
||||
/// </summary>
|
||||
public Dictionary<string, string> mMapWriteLink { get; set; } = new Dictionary<string, string>();
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Base del NameSpace usato per le funzionalità di translate (parametri ACT-SET), da inserire come PRE
|
||||
|
||||
@@ -45,6 +45,7 @@ CLI_INST=SteamWareSim
|
||||
;STARTLIST=SIM_DP_01
|
||||
|
||||
;NB: mettere copy always ai file di conf x fare test...
|
||||
STARTLIST=FTP_SONATEST
|
||||
;STARTLIST=FTP_SONATEST
|
||||
STARTLIST=SIMUL_01
|
||||
|
||||
MAXCNC=10
|
||||
@@ -4,7 +4,7 @@ CNCTYPE=SIMULA
|
||||
PING_MS_TIMEOUT=500
|
||||
MinDeltaSec=5
|
||||
EnableRedisQueue=true
|
||||
IOB_NAME=TFT_RAMA_001
|
||||
;IOB_NAME=TFT_RAMA_001
|
||||
|
||||
[MACHINE]
|
||||
VENDOR=STEAMWARE
|
||||
|
||||
@@ -9,6 +9,15 @@
|
||||
"size": 20,
|
||||
"displOrdinal": 1
|
||||
},
|
||||
"setArtNum": {
|
||||
"name": "setArtNum",
|
||||
"description": "# Num Articolo",
|
||||
"memAddr": "DB150.DBB112",
|
||||
"tipoMem": "Int",
|
||||
"index": 112,
|
||||
"size": 4,
|
||||
"displOrdinal": 1
|
||||
},
|
||||
"setComm": {
|
||||
"name": "setComm",
|
||||
"description": "Commessa",
|
||||
@@ -18,6 +27,15 @@
|
||||
"size": 20,
|
||||
"displOrdinal": 2
|
||||
},
|
||||
"setCommNum": {
|
||||
"name": "setCommNum",
|
||||
"description": "# NumCommessa",
|
||||
"memAddr": "DB150.DBB132",
|
||||
"tipoMem": "Int",
|
||||
"index": 132,
|
||||
"size": 4,
|
||||
"displOrdinal": 2
|
||||
},
|
||||
"setPzComm": {
|
||||
"name": "setPzComm",
|
||||
"description": "Qta Richiesta",
|
||||
@@ -378,11 +396,15 @@
|
||||
"path-confSetup": "C:\\MesData\\Setup\\setupConsumi.json",
|
||||
"replace-<Variant>": "<Variant>{{PODL}}",
|
||||
"replace-<Info1>": "<Info1>Kg{{Qty}} | {{Note}}"
|
||||
} //,
|
||||
}, //,
|
||||
//"BaseKeyTranslate": "ns=2;s=RamosaETN21.RamosaCJ2",
|
||||
//"RecipeKeyTranslate": {
|
||||
// "Present Value.General Fan": "Recipe.Chain Spped",
|
||||
// "Present Value.Bottom Overfeeding": "Recipe.Bottom Overfeeding",
|
||||
// "Present Value.Top Overfeeding": "Recipe.Top Overfeeding"
|
||||
//}
|
||||
"mMapWriteLink": {
|
||||
"setArt": "setArtNum",
|
||||
"setComm": "setCommNum"
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ MODEL=CROSSFLEX A1600
|
||||
|
||||
[CNC]
|
||||
; FANUC!
|
||||
IP=192.168.1.12
|
||||
IP=192.168.214.100\
|
||||
PORT=9000
|
||||
GETPRGNAME=true
|
||||
|
||||
@@ -24,13 +24,13 @@ CMDADV1=?valore=
|
||||
CMDREBO=/sendReboot.aspx?idxMacchina=
|
||||
|
||||
[MEMORY]
|
||||
; Green 11.4 | Red: Y11.7 | Yellow: Y11.6 (bianco, eventualmente in lampeggio) | Emerg: X8.4 |
|
||||
; Green 11.4 | Red: Y11.7 | Yellow: Y11.6 (bianco, eventualmente in lampeggio) | Emerg: X8.4 (negata)|
|
||||
;BIT0=CONN
|
||||
BIT1=Y11.4
|
||||
BIT2=PZCOUNT.PAR.6711
|
||||
BIT3=Y11.7
|
||||
BIT4=Y11.6
|
||||
BIT5=X8.4
|
||||
BIT5=!X8.4
|
||||
AREAD_START=0
|
||||
AREAD_SIZE=9999
|
||||
AREAG_SIZE=48
|
||||
|
||||
@@ -147,8 +147,8 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>ExtLib\krcc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MapoSDK, Version=6.14.2309.1908, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MapoSDK.6.14.2309.1908\lib\MapoSDK.dll</HintPath>
|
||||
<Reference Include="MapoSDK, Version=6.14.2411.518, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MapoSDK.6.14.2411.518\lib\MapoSDK.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MathNet.Numerics, Version=4.15.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MathNet.Numerics.4.15.0\lib\net461\MathNet.Numerics.dll</HintPath>
|
||||
@@ -515,7 +515,9 @@
|
||||
<None Include="DATA\CONF\FOV098.ini" />
|
||||
<None Include="DATA\CONF\FOV099.ini" />
|
||||
<None Include="DATA\CONF\VL27.ini" />
|
||||
<None Include="DATA\CONF\SIMUL_01.json" />
|
||||
<None Include="DATA\CONF\SIMUL_01.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="DATA\CONF\STEL_210.ini" />
|
||||
<None Include="DATA\CONF\STEL_210.json" />
|
||||
<None Include="DATA\CONF\TFT_RAMA_001.ini">
|
||||
@@ -837,7 +839,9 @@
|
||||
<None Include="DATA\CONF\SIM_DP_01.ini">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="DATA\CONF\SIMUL_01.ini" />
|
||||
<None Include="DATA\CONF\SIMUL_01.ini">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="DATA\CONF\SIMUL_02.ini" />
|
||||
<None Include="DATA\CONF\3006.ini" />
|
||||
<None Include="DATA\CONF\3007.ini" />
|
||||
|
||||
+234
-17
@@ -835,6 +835,46 @@ namespace IOB_WIN_NEXT.Iob
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// URL per recupero traduzione CodArt in numerico...
|
||||
/// </summary>
|
||||
public string urlGetNumArt
|
||||
{
|
||||
get
|
||||
{
|
||||
string answ = "";
|
||||
try
|
||||
{
|
||||
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getArtNum/{cIobConf.codIOB}";
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
lgError(exc, "Errore in composizione urlGetNumArt");
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// URL per recupero traduzione CodComm in numerico...
|
||||
/// </summary>
|
||||
public string urlGetNumComm
|
||||
{
|
||||
get
|
||||
{
|
||||
string answ = "";
|
||||
try
|
||||
{
|
||||
answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/getXdlNum/{cIobConf.codIOB}";
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
lgError(exc, "Errore in composizione urlGetNumComm");
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// URL per recupero idle time IOB...
|
||||
/// </summary>
|
||||
@@ -1469,6 +1509,7 @@ namespace IOB_WIN_NEXT.Iob
|
||||
{
|
||||
bool taskOk = false;
|
||||
string taskVal = "";
|
||||
string newVal = "";
|
||||
// cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4
|
||||
foreach (var item in task2exe)
|
||||
{
|
||||
@@ -1509,6 +1550,69 @@ namespace IOB_WIN_NEXT.Iob
|
||||
|
||||
break;
|
||||
|
||||
case taskType.setArtNum:
|
||||
// in primis faccio una chiamata per tutta la tab SE fosse vuoto il dict di traduzione
|
||||
if (DictNumArt == null || DictNumArt.Count == 0)
|
||||
{
|
||||
getNumArt("");
|
||||
}
|
||||
// chiamo server x avere decodifica valore INT
|
||||
newVal = getNumArt(item.Value);
|
||||
// procedo come il resto cercando mappatura in memMap: recupero dati
|
||||
// da memMap...
|
||||
if (memMap != null && memMap.mMapWrite != null)
|
||||
{
|
||||
if (memMap.mMapWrite.ContainsKey(item.Key))
|
||||
{
|
||||
dataConf currMem = memMap.mMapWrite[item.Key];
|
||||
string addr = currMem.memAddr;
|
||||
taskVal = $"SET task: {item.Key} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
|
||||
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
|
||||
memMap.mMapWrite[item.Key].value = newVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
taskVal = $"NO MemMap found, SET task: {item.Key} --> {newVal} ({item.Value})";
|
||||
}
|
||||
|
||||
// salvo in currProd..
|
||||
saveProdData(new KeyValuePair<string, string>(item.Key, newVal));
|
||||
break;
|
||||
|
||||
case taskType.setCommNum:
|
||||
// chiamo server x avere decodifica valore INT
|
||||
newVal = getNumComm(item.Value);
|
||||
// procedo come il resto cercando mappatura in memMap: recupero dati
|
||||
// da memMap...
|
||||
if (memMap != null && memMap.mMapWrite != null)
|
||||
{
|
||||
if (memMap.mMapWrite.ContainsKey(item.Key))
|
||||
{
|
||||
dataConf currMem = memMap.mMapWrite[item.Key];
|
||||
string addr = currMem.memAddr;
|
||||
taskVal = $"SET task: {item.Key} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
|
||||
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
|
||||
memMap.mMapWrite[item.Key].value = newVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
taskVal = $"NO MemMap found, SET task: {item.Key} --> {newVal} ({item.Value})";
|
||||
}
|
||||
|
||||
// salvo in currProd..
|
||||
saveProdData(new KeyValuePair<string, string>(item.Key, newVal));
|
||||
break;
|
||||
|
||||
case taskType.forceResetPzCount:
|
||||
// reset contapezzi inizio setup
|
||||
taskOk = resetContapezziPLC();
|
||||
@@ -2105,6 +2209,25 @@ namespace IOB_WIN_NEXT.Iob
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cerca se esiste un link tra aree di memoria in write e lo restituisce
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public string getOptWriteLink(string key)
|
||||
{
|
||||
string answ = "";
|
||||
// controllo SE HO il parametro
|
||||
if (memMap != null && memMap.mMapWriteLink != null && memMap.mMapWriteLink.Count > 0)
|
||||
{
|
||||
if (memMap.mMapWriteLink.ContainsKey(key))
|
||||
{
|
||||
answ = memMap.mMapWriteLink[key];
|
||||
}
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce info OVERRIDES
|
||||
/// </summary>
|
||||
@@ -2939,6 +3062,7 @@ namespace IOB_WIN_NEXT.Iob
|
||||
public Dictionary<string, string> processTask(Dictionary<string, string> task2exe)
|
||||
{
|
||||
Dictionary<string, string> taskDone = new Dictionary<string, string>();
|
||||
Dictionary<string, string> task2Add = new Dictionary<string, string>();
|
||||
// eseguo realmente solo se NON disabilitata questa gestione (caso doppio PLC/HMI)...
|
||||
if (!cIobConf.disableExeTask)
|
||||
{
|
||||
@@ -2950,6 +3074,21 @@ namespace IOB_WIN_NEXT.Iob
|
||||
{
|
||||
idTask++;
|
||||
lgInfo($"[{idTask:00}] - {item.Key} --> {item.Value}");
|
||||
// verifico SE il task abbia un duplo writeLink e nel caso lo aggiungo...
|
||||
var linkVal = getOptWriteLink(item.Key);
|
||||
if (!string.IsNullOrEmpty(linkVal))
|
||||
{
|
||||
// aggiungo a task2add...
|
||||
task2Add.Add(linkVal, item.Value);
|
||||
}
|
||||
}
|
||||
// se ho task Link da aggiungere li aggiungo!
|
||||
if (task2Add.Count > 0)
|
||||
{
|
||||
foreach (var item in task2Add)
|
||||
{
|
||||
task2exe.Add(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
// chiamo procedura esecutiva (diversa x ogni IOB)
|
||||
taskDone = executeTasks(task2exe);
|
||||
@@ -6007,23 +6146,6 @@ namespace IOB_WIN_NEXT.Iob
|
||||
return answ;
|
||||
}
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Traduzione del valore chiave configurato dal dizionario RecipeKeyTranslate + BaseKeyTranslate
|
||||
/// </summary>
|
||||
/// <param name="currKey"></param>
|
||||
/// <returns></returns>
|
||||
protected string RecipeKeyDecod(Dictionary<string, string> keyDict, string currKey)
|
||||
{
|
||||
string answ = currKey;
|
||||
if (keyDict != null)
|
||||
{
|
||||
answ = keyDict[currKey];
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected void raiseRefresh(newDisplayData currDispData)
|
||||
{
|
||||
if (currDispData != null)
|
||||
@@ -7638,6 +7760,101 @@ namespace IOB_WIN_NEXT.Iob
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera valore numerico ARTICOLO x salvataggio valori INT
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
protected string getNumArt(string value)
|
||||
{
|
||||
string answ = "";
|
||||
// cerco in dizionario corrente...
|
||||
if (DictNumArt.Count > 0 && DictNumArt.ContainsKey(value))
|
||||
{
|
||||
answ = DictNumArt[value];
|
||||
}
|
||||
// altrimenti chiamo servizio
|
||||
else
|
||||
{
|
||||
// chiamo MP-IO server
|
||||
if (checkServerAlive)
|
||||
{
|
||||
string url2call = $"{urlGetNumArt}?CodArt={value}";
|
||||
if (verboseLog)
|
||||
{
|
||||
lgInfo("chiamata URL " + url2call);
|
||||
}
|
||||
var rawData = utils.callUrlNow(url2call);
|
||||
// deserializzo e recupero KVP...
|
||||
var dictArtSrv = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawData);
|
||||
// se fosse una chiamata con valore vuoto --> salvo intera tabella...
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
DictNumArt = dictArtSrv;
|
||||
}
|
||||
// altrimenti aggiungo a dizionario
|
||||
else
|
||||
{
|
||||
foreach (var item in dictArtSrv)
|
||||
{
|
||||
if (!DictNumArt.ContainsKey(item.Key))
|
||||
{
|
||||
DictNumArt.Add(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// ora cerco valore e restituisco
|
||||
if (DictNumArt.ContainsKey(value))
|
||||
{
|
||||
answ = DictNumArt[value];
|
||||
}
|
||||
}
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
protected Dictionary<string, string> DictNumArt { get; set; } = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Recupera valore numerico COMMESSA x salvataggio valori INT
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
protected string getNumComm(string value)
|
||||
{
|
||||
string answ = "";
|
||||
// chiamo MP-IO server
|
||||
if (checkServerAlive)
|
||||
{
|
||||
string url2call = $"{urlGetNumComm}?CodXdl={value}";
|
||||
if (verboseLog)
|
||||
{
|
||||
lgInfo("chiamata URL " + url2call);
|
||||
}
|
||||
// è direttamente il risultato!
|
||||
value = utils.callUrlNow(url2call);
|
||||
}
|
||||
// restituisco
|
||||
return answ;
|
||||
}
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Traduzione del valore chiave configurato dal dizionario RecipeKeyTranslate + BaseKeyTranslate
|
||||
/// </summary>
|
||||
/// <param name="currKey"></param>
|
||||
/// <returns></returns>
|
||||
protected string RecipeKeyDecod(Dictionary<string, string> keyDict, string currKey)
|
||||
{
|
||||
string answ = currKey;
|
||||
if (keyDict != null)
|
||||
{
|
||||
answ = keyDict[currKey];
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// recupera valore salvato in persistence layer (se non c'è crea...)
|
||||
/// </summary>
|
||||
|
||||
@@ -221,6 +221,7 @@ namespace IOB_WIN_NEXT.Iob
|
||||
if (!disableSimStatus)
|
||||
{
|
||||
string taskVal = "";
|
||||
string newVal = "";
|
||||
// verifico non sia null
|
||||
if (task2exe != null)
|
||||
{
|
||||
@@ -228,6 +229,7 @@ namespace IOB_WIN_NEXT.Iob
|
||||
foreach (var item in task2exe)
|
||||
{
|
||||
taskVal = "";
|
||||
newVal = "";
|
||||
// converto richiesta in enum...
|
||||
taskType tName = taskType.nihil;
|
||||
Enum.TryParse(item.Key, out tName);
|
||||
@@ -260,6 +262,68 @@ namespace IOB_WIN_NEXT.Iob
|
||||
}
|
||||
}
|
||||
break;
|
||||
case taskType.setArtNum:
|
||||
// in primis faccio una chiamata per tutta la tab SE fosse vuoto il dict di traduzione
|
||||
if (DictNumArt == null || DictNumArt.Count == 0)
|
||||
{
|
||||
getNumArt("");
|
||||
}
|
||||
// chiamo server x avere decodifica valore INT
|
||||
newVal = getNumArt(item.Value);
|
||||
// procedo come il resto cercando mappatura in memMap: recupero dati
|
||||
// da memMap...
|
||||
if (memMap != null && memMap.mMapWrite != null)
|
||||
{
|
||||
if (memMap.mMapWrite.ContainsKey(item.Key))
|
||||
{
|
||||
dataConf currMem = memMap.mMapWrite[item.Key];
|
||||
string addr = currMem.memAddr;
|
||||
taskVal = $"SET task: {item.Key} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
|
||||
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
|
||||
memMap.mMapWrite[item.Key].value = newVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
taskVal = $"NO MemMap found, SET task: {item.Key} --> {newVal} ({item.Value})";
|
||||
}
|
||||
|
||||
// salvo in currProd..
|
||||
saveProdData(new KeyValuePair<string, string>(item.Key, newVal));
|
||||
break;
|
||||
|
||||
case taskType.setCommNum:
|
||||
// chiamo server x avere decodifica valore INT
|
||||
newVal = getNumComm(item.Value);
|
||||
// procedo come il resto cercando mappatura in memMap: recupero dati
|
||||
// da memMap...
|
||||
if (memMap != null && memMap.mMapWrite != null)
|
||||
{
|
||||
if (memMap.mMapWrite.ContainsKey(item.Key))
|
||||
{
|
||||
dataConf currMem = memMap.mMapWrite[item.Key];
|
||||
string addr = currMem.memAddr;
|
||||
taskVal = $"SET task: {item.Key} --> {newVal} | mem: {currMem.memAddr} - {currMem.size} byte";
|
||||
// salvo il nuovo valore nella memoria... così prox invio lo trasmetterà
|
||||
memMap.mMapWrite[item.Key].value = newVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
taskVal = $"NO DATA MEM, SET task: {item.Key} --> {newVal} ({item.Value})";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
taskVal = $"NO MemMap found, SET task: {item.Key} --> {newVal} ({item.Value})";
|
||||
}
|
||||
|
||||
// salvo in currProd..
|
||||
saveProdData(new KeyValuePair<string, string>(item.Key, newVal));
|
||||
break;
|
||||
|
||||
case taskType.nihil:
|
||||
case taskType.fixStopSetup:
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<package id="EntityFramework" version="6.4.4" targetFramework="net462" />
|
||||
<package id="FluentFTP" version="51.1.0" targetFramework="net462" />
|
||||
<package id="Fody" version="6.8.0" targetFramework="net462" developmentDependency="true" />
|
||||
<package id="MapoSDK" version="6.14.2309.1908" targetFramework="net462" />
|
||||
<package id="MapoSDK" version="6.14.2411.518" targetFramework="net462" />
|
||||
<package id="MathNet.Numerics" version="4.15.0" targetFramework="net462" />
|
||||
<package id="Microsoft.CodeAnalysis.NetAnalyzers" version="6.0.0" targetFramework="net462" developmentDependency="true" />
|
||||
<package id="Microsoft.Extensions.Logging.Abstractions" version="2.1.0" targetFramework="net462" />
|
||||
|
||||
Reference in New Issue
Block a user