1341 lines
54 KiB
C#
1341 lines
54 KiB
C#
using EasyModbus;
|
|
using IOB_UT_NEXT;
|
|
using MapoSDK;
|
|
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net.NetworkInformation;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
|
|
namespace IOB_WIN_NEXT
|
|
{
|
|
/* --------------------------------------------------------------------------------
|
|
* Controlli ModBusTCP COMECA
|
|
* - protocollo ModBus TCP
|
|
*
|
|
* -------------------------------------------------------------------------------- */
|
|
|
|
public class IobModbusTCP : IobGeneric
|
|
{
|
|
#region Private Fields
|
|
|
|
/// <summary>
|
|
/// Valore delta x gestione min/MAX e valore rilevato
|
|
/// </summary>
|
|
private double deltaVal = 0;
|
|
|
|
#endregion Private Fields
|
|
|
|
#region Protected Fields
|
|
|
|
protected ModbusClient currPLC;
|
|
|
|
/// <summary>
|
|
/// Copia locale dei valori in Holding Registry, come array chiave (int) valori int[] letti tramite ModBus, da convertire secondo tipo
|
|
/// </summary>
|
|
protected Dictionary<int, int[]> HoldingRegisterLUT = new Dictionary<int, int[]>();
|
|
|
|
/// <summary>
|
|
/// Ultimo controllo ping x evitare ping flood...
|
|
/// </summary>
|
|
protected DateTime lastPingConn = DateTime.Now.AddMinutes(-10);
|
|
|
|
/// <summary>
|
|
/// Esito ultimo ping
|
|
/// </summary>
|
|
protected bool lastPingOk = false;
|
|
|
|
/// <summary>
|
|
/// Setup blocchi memorie read (indirizzo inizio, size)
|
|
/// </summary>
|
|
protected Dictionary<int, int> memSetR = new Dictionary<int, int>();
|
|
|
|
/// <summary>
|
|
/// parametri di connessione
|
|
/// </summary>
|
|
protected connParamModBusTCP parametri;
|
|
|
|
/// <summary>
|
|
/// Oggetto cronometro x test vari...
|
|
/// </summary>
|
|
protected Stopwatch sw = new Stopwatch();
|
|
|
|
#endregion Protected Fields
|
|
|
|
#region Public Constructors
|
|
|
|
/// Classe base con i metodi x ModBusTCP
|
|
/// </summary>
|
|
/// <param name="caller"></param>
|
|
/// <param name="adpConf"></param>
|
|
public IobModbusTCP(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf)
|
|
{
|
|
lgInfo("NEW IOB ModBus TCP");
|
|
memMap = new plcMemMap();
|
|
if (IOBConf != null)
|
|
{
|
|
// gestione invio ritardato contapezzi
|
|
pzCountDelay = utils.CRI("pzCountDelay");
|
|
lastPzCountSend = DateTime.Now;
|
|
lastWarnODL = DateTime.Now;
|
|
// inizializzo parametri...
|
|
parametri = new connParamModBusTCP()
|
|
{
|
|
ipAdrr = "127.0.0.1",
|
|
port = 502,
|
|
pingMsTimeout = IOBConf.pingMsTimeout,
|
|
memAddrRead = "40001",
|
|
memAddrWrite = "41001",
|
|
memSizeRead = 0,
|
|
memSizeWrite = 0
|
|
};
|
|
setParamPlc();
|
|
|
|
// salvo info su conf IOB...
|
|
string iobConfSer = "";
|
|
try
|
|
{
|
|
iobConfSer = JsonConvert.SerializeObject(IOBConf, Formatting.Indented);
|
|
}
|
|
catch
|
|
{ }
|
|
// finito!
|
|
lgInfo($"Init IOB, con {iobConfSer}");
|
|
}
|
|
else
|
|
{
|
|
lgError("Impossibile avviare, IOBConf nullo/non valido!");
|
|
}
|
|
}
|
|
|
|
#endregion Public Constructors
|
|
|
|
#region Protected Properties
|
|
|
|
protected bool hasAlarms
|
|
{
|
|
get
|
|
{
|
|
bool answ = false;
|
|
int numErrors = 0;
|
|
int currStatus = 0;
|
|
int[] listInt = new int[2];
|
|
if (alarmMaps != null)
|
|
{
|
|
// leggo a ciclo le aree degli allarmi CONFIGURATI, se ne trovo --> segnalo allarme...
|
|
foreach (var item in alarmMaps)
|
|
{
|
|
// in primis decremento eventuali blink...
|
|
item.decreaseBlinkCounter();
|
|
// banchi in array Int16 --> scompongo
|
|
for (int i = 0; i < item.size / 2; i++)
|
|
{
|
|
// verifico se usare LUT
|
|
bool useLUT = memSetR != null && memSetR.Count > 0;
|
|
if (useLUT)
|
|
{
|
|
int lutAddress = 40000 + item.index;
|
|
if (HoldingRegisterLUT.ContainsKey(lutAddress))
|
|
{
|
|
try
|
|
{
|
|
listInt = HoldingRegisterLUT[lutAddress];
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Errore in lettura da HoldingRegisterLUT per indirizzo {lutAddress} | item {item.memAddr}{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
listInt = readInputReg(item.index, item.size);
|
|
}
|
|
currStatus = ModbusClient.ConvertRegistersToInt(listInt);
|
|
//currStatus = listInt[0];
|
|
|
|
// verifico SE sia variato... confronto allarmi filtrato stile blink per bit status:
|
|
// - allarmi che iniziano per # IGNORATI
|
|
// - altri allarmi con un countdown da MAX_COUNTER_BLINK a 0 per il fronte di discesa
|
|
if (item.isChanged(i, (uint)currStatus))
|
|
{
|
|
answ = currStatus > 0;
|
|
if (answ)
|
|
{
|
|
numErrors++;
|
|
}
|
|
// registro gli allarmi attivi e trasmetto...
|
|
if (sendAlarmVariations(item.memAddr, i, item.alarmsState[i], (uint)(item.alarmsMask[i] & currStatus), item.messages))
|
|
{
|
|
// se inviato --> salvo stato da current...
|
|
item.updStatusVal(i, (uint)(item.alarmsMask[i] & currStatus));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dizionario delle ultime operazioni di scrittura per OGNI memoria (in modo che fa log ogni x sec...)
|
|
/// </summary>
|
|
protected Dictionary<string, DateTime> lastMemWrite { get; set; } = new Dictionary<string, DateTime>();
|
|
|
|
#endregion Protected Properties
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>
|
|
/// converte valore in tipo desiderato
|
|
/// </summary>
|
|
/// <param name="listInt"></param>
|
|
/// <param name="size"></param>
|
|
/// <param name="tipoMem"></param>
|
|
/// <returns></returns>
|
|
private static double convertFromReg(int[] listInt, int size, plcDataType tipoMem)
|
|
{
|
|
double valore = 0;
|
|
//verifico se sia INT o real x convertire...
|
|
switch (tipoMem)
|
|
{
|
|
case plcDataType.Real:
|
|
if (size == 4)
|
|
{
|
|
valore = ModbusClient.ConvertRegistersToDouble(listInt);
|
|
}
|
|
else if (size == 2)
|
|
{
|
|
valore = ModbusClient.ConvertRegistersToFloat(listInt);
|
|
}
|
|
break;
|
|
|
|
case plcDataType.Int:
|
|
default:
|
|
if (size == 4)
|
|
{
|
|
valore = ModbusClient.ConvertRegistersToLong(listInt);
|
|
}
|
|
else if (size == 2)
|
|
{
|
|
valore = ModbusClient.ConvertRegistersToInt(listInt);
|
|
}
|
|
break;
|
|
}
|
|
return valore;
|
|
}
|
|
|
|
private static double getScaledDouble(dataConf currMem)
|
|
{
|
|
double valDouble;
|
|
// prima faccio eventuale fattore di scala...
|
|
double.TryParse(currMem.value, out valDouble);
|
|
if (currMem.factor > 1)
|
|
{
|
|
valDouble = valDouble * (double)currMem.factor;
|
|
}
|
|
|
|
return valDouble;
|
|
}
|
|
|
|
private static int getScaledInt(dataConf currMem)
|
|
{
|
|
int valInt;
|
|
// prima faccio eventuale fattore di scala...
|
|
int.TryParse(currMem.value, out valInt);
|
|
if (currMem.factor > 1)
|
|
{
|
|
valInt = valInt * currMem.factor;
|
|
}
|
|
|
|
return valInt;
|
|
}
|
|
|
|
private static uint getScaledUInt(dataConf currMem)
|
|
{
|
|
uint valUInt;
|
|
// prima faccio eventuale fattore di scala...
|
|
uint.TryParse(currMem.value, out valUInt);
|
|
if (currMem.factor > 1)
|
|
{
|
|
valUInt = valUInt * (uint)currMem.factor;
|
|
}
|
|
|
|
return valUInt;
|
|
}
|
|
|
|
/// <summary>
|
|
/// se si disconnette --> processo tentativo riconnessione..
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
private void CurrPLC_ConnectedChanged(object sender)
|
|
{
|
|
if (!currPLC.Connected)
|
|
{
|
|
tryDisconnect();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupero dati da singole letture
|
|
/// </summary>
|
|
/// <param name="memItemList">Valori da processare</param>
|
|
/// <param name="outVal">Dizionario valori in uscita</param>
|
|
/// <returns>Errori di lettura</returns>
|
|
private Dictionary<string, dataConfTSVC> getDataDictionary(Dictionary<string, dataConfTSVC> memItemList, bool useLUT, ref Dictionary<string, string> outVal)
|
|
{
|
|
Dictionary<string, dataConfTSVC> readErrorList = new Dictionary<string, dataConfTSVC>();
|
|
// inizializzo i valori
|
|
bool[] listBool = new bool[1];
|
|
int[] listInt = new int[2];
|
|
// procedo x ogni valore configurato...
|
|
foreach (var item in memItemList)
|
|
{
|
|
// attesa 50 ms prima di procedere x evitare burst dati
|
|
if (!useLUT)
|
|
{
|
|
Thread.Sleep(50);
|
|
}
|
|
double valore = 0;
|
|
double valoreScal = 0;
|
|
bool dataOk = false;
|
|
// in primis DEVO determinare di quale TIPO di valore ho bisogno... dalla PRIMA cifra di memAddr...
|
|
modBusAddrType memAddrType = getAddrType(item.Value.memAddr);
|
|
// in base al tipo leggo array...
|
|
switch (memAddrType)
|
|
{
|
|
case modBusAddrType.Coil:
|
|
listBool = readCoil(item.Value.index, item.Value.size);
|
|
valore = listBool[0] ? 1 : 0;
|
|
break;
|
|
|
|
case modBusAddrType.DiscreteInput:
|
|
listBool = readDiscrInputs(item.Value.index, item.Value.size);
|
|
valore = listBool[0] ? 1 : 0;
|
|
break;
|
|
|
|
case modBusAddrType.InputRegister:
|
|
listInt = readInputReg(item.Value.index, item.Value.size);
|
|
valore = convertFromReg(listInt, item.Value.size, item.Value.tipoMem);
|
|
|
|
break;
|
|
|
|
case modBusAddrType.HoldingRegister:
|
|
if (useLUT)
|
|
{
|
|
int lutAddress = 40000 + item.Value.index;
|
|
if (HoldingRegisterLUT.ContainsKey(lutAddress))
|
|
{
|
|
try
|
|
{
|
|
listInt = HoldingRegisterLUT[lutAddress];
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Errore in lettura da HoldingRegisterLUT per indirizzo {lutAddress} | item {item.Key}{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
listInt = readHoldReg(item.Value.index - 1, item.Value.size);
|
|
}
|
|
valore = convertFromReg(listInt, item.Value.size, item.Value.tipoMem);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
// verifica limite... con delta da impianto
|
|
dataOk = (valore > (item.Value.minVal + deltaVal) && valore < (item.Value.maxVal - deltaVal));
|
|
if (dataOk)
|
|
{
|
|
// moltiplico x fattore conversione...
|
|
valoreScal = valore * item.Value.factor;
|
|
saveValue(ref outVal, valoreScal, item.Key);
|
|
lgInfo($"getDynData: valore ricevuto | {item.Key} | val: {valore} | valoreScal: {valoreScal}");
|
|
}
|
|
else
|
|
{
|
|
lgError($"getDynData: valore scartato x limiti min/max | {item.Key} | val: {valore} | valoreScal: {valoreScal} | min-max: {item.Value.minVal}-{item.Value.maxVal} | deltaVal: {deltaVal}");
|
|
readErrorList.Add(item.Key, item.Value);
|
|
lgInfo($"--> rimesso in coda lettura | parametro: {item.Key} | index: {item.Value.index} | size: {item.Value.size}");
|
|
}
|
|
}
|
|
|
|
return readErrorList;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifica SE sia il caso di fare il log della memoria indicata
|
|
/// </summary>
|
|
/// <param name="memAddrWrite"></param>
|
|
/// <param name="logValue"></param>
|
|
private void maybeLogWrite(string memAddrWrite, string logValue)
|
|
{
|
|
bool doWrite = true;
|
|
DateTime adesso = DateTime.Now;
|
|
if (!lastMemWrite.ContainsKey(memAddrWrite))
|
|
{
|
|
lastMemWrite.Add(memAddrWrite, adesso.AddMinutes(-1));
|
|
}
|
|
// ora mi leggo valore ultimas crittura e confronto con adesso
|
|
try
|
|
{
|
|
doWrite = (lastMemWrite[memAddrWrite].AddSeconds(vetoSeconds) < adesso);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in maybeLogWrite{Environment.NewLine}{exc}");
|
|
}
|
|
// se encessario --> LOG!
|
|
if (doWrite)
|
|
{
|
|
lgInfo(logValue);
|
|
lastMemWrite[memAddrWrite] = adesso;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua lettura blocco memoria indicato e lo salva in copia locale HoldingRegisterLUT
|
|
/// </summary>
|
|
/// <param name="startAddr">Indirizzo di aprtenza</param>
|
|
/// <param name="numReg">NUm registri da leggere (max 120)</param>
|
|
private void readBlockHoldingReg(int startAddr, int numReg)
|
|
{
|
|
bool allOk = false;
|
|
if (currPLC.Connected)
|
|
{
|
|
allOk = true;
|
|
// fix massima lunghezza pacchetto
|
|
if (numReg > 120)
|
|
{
|
|
lgError($"Attenzione richiesta lettura blocco troppo grande: numReg {numReg} --> 120");
|
|
numReg = 120;
|
|
}
|
|
// si lavora rispetto all'indirizzo base 40001
|
|
int baseAddr = 40001;
|
|
int[] rawData = new int[2];
|
|
try
|
|
{
|
|
stopwatch.Restart();
|
|
rawData = currPLC.ReadHoldingRegisters(startAddr - baseAddr, numReg);
|
|
stopwatch.Stop();
|
|
lgInfo($"Lettura in blocco HoldingRegisters| startAddr: {startAddr} | numReg {numReg} | rawData.lenght: {rawData.Length} | {stopwatch.ElapsedMilliseconds}ms");
|
|
// salvo in LUT la versione ESPLOSA 2 byte alla volta...
|
|
if (rawData.Length > 0)
|
|
{
|
|
for (int i = 0; i < rawData.Length / 2; i++)
|
|
{
|
|
int[] thisSet = new int[2];
|
|
if (rawData.Length >= (i + 1) * 2)
|
|
{
|
|
Array.Copy(rawData, i * 2, thisSet, 0, 2);
|
|
// salvo nel registro...
|
|
int currAddr = startAddr + i * 2;
|
|
if (HoldingRegisterLUT.ContainsKey(currAddr))
|
|
{
|
|
HoldingRegisterLUT[currAddr] = thisSet;
|
|
}
|
|
else
|
|
{
|
|
HoldingRegisterLUT.Add(currAddr, thisSet);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
currReadErrors++;
|
|
allOk = false;
|
|
lgError($"Impossibile copiare dati, array di origine troppo corto: rawData.lenght: {rawData.Length} | base addr richiesto i*2: {i * 2} | errori currReadErrors: {currReadErrors}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
currReadErrors++;
|
|
allOk = false;
|
|
lgError($"Eccezione in readBlockHoldingReg | currReadErrors: {currReadErrors}{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgError($"Attenzione, plc disconnesso... currReadErrors: {currReadErrors}");
|
|
tryDisconnect();
|
|
}
|
|
|
|
// se tutto ok --> riduco contatore errori di 1...
|
|
if (allOk)
|
|
{
|
|
currReadErrors = currReadErrors >= 1 ? currReadErrors-- : 0;
|
|
}
|
|
else
|
|
{
|
|
// se > max errori --> disconnetto
|
|
if (currReadErrors > maxReadErrors)
|
|
{
|
|
lgInfo($"Superato limite errori Read ({currReadErrors}) --> tryDisconnect");
|
|
currReadErrors = 0;
|
|
tryDisconnect();
|
|
}
|
|
else
|
|
{
|
|
// altrimenti pausa forzata
|
|
Thread.Sleep(300);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion Private Methods
|
|
|
|
#region Protected Methods
|
|
|
|
/// <summary>
|
|
/// Decodifica il resto dell'area x i dati accessori (allarmi, ...)
|
|
/// </summary>
|
|
protected virtual void decodeOtherData()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua decodifica aree memoria alla bitmap usata x MAPO/GWMS
|
|
/// - per lo scopo specifico IN REALTA' non conta lo stato macchina.... ma lo inviamo lo stesso
|
|
/// </summary>
|
|
protected virtual void decodeToBaseBitmap()
|
|
{
|
|
// init a zero...
|
|
B_input = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override metodo x scrittura parametri su PLC
|
|
/// </summary>
|
|
/// <param name="updatedPar"></param>
|
|
protected override void plcWriteParams(ref List<objItem> updatedPar)
|
|
{
|
|
dataConf currMem = null;
|
|
int byteSize = 0;
|
|
int[] CurrVal = new int[1];
|
|
string memAddrWrite = "";
|
|
bool fatto = false;
|
|
string serObj = "";
|
|
if (updatedPar != null)
|
|
{
|
|
// controllo i parametri... ne gestisco 4...
|
|
foreach (var item in updatedPar)
|
|
{
|
|
try
|
|
{
|
|
memAddrWrite = "";
|
|
int valInt = 0;
|
|
uint valUInt = 0;
|
|
double valDouble = 0;
|
|
// cerco in area memMapWrite...
|
|
if (memMap.mMapWrite.ContainsKey(item.uid))
|
|
{
|
|
// recupero!
|
|
currMem = memMap.mMapWrite[item.uid];
|
|
byteSize = currMem.size;
|
|
memAddrWrite = currMem.memAddr;
|
|
CurrVal = new int[byteSize];
|
|
// faccio preliminarmente upsertKey...
|
|
upsertKey(currMem.name, currMem.value);
|
|
serObj = JsonConvert.SerializeObject(item, Formatting.Indented);
|
|
lgInfo($"Inizio processing plcWriteParams per {currMem.name} | valore richiesto {currMem.value}{Environment.NewLine}---------------UPDATED PARAM---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------");
|
|
serObj = JsonConvert.SerializeObject(currMem, Formatting.Indented);
|
|
lgInfo($"---------------MEMORY CONTENT---------------{Environment.NewLine}{serObj}{Environment.NewLine}---------------");
|
|
switch (currMem.tipoMem)
|
|
{
|
|
case plcDataType.Boolean:
|
|
break;
|
|
|
|
case plcDataType.Int:
|
|
valInt = getScaledInt(currMem);
|
|
CurrVal = ModbusClient.ConvertIntToRegisters(valInt);
|
|
fatto = writeInputReg(currMem.index, CurrVal);
|
|
break;
|
|
|
|
case plcDataType.DInt:
|
|
valUInt = getScaledUInt(currMem);
|
|
CurrVal = ModbusClient.ConvertLongToRegisters(valInt);
|
|
fatto = writeInputReg(currMem.index, CurrVal);
|
|
break;
|
|
|
|
//case plcDataType.Word:
|
|
// valUInt = getScaledUInt(currMem);
|
|
// saveWordOnMemBlock(ref MemBlock, 0, valInt.ToString());
|
|
// break;
|
|
|
|
//case plcDataType.DWord:
|
|
// valUInt = getScaledUInt(currMem);
|
|
// saveDWordOnMemBlock(ref MemBlock, 0, valInt.ToString());
|
|
// break;
|
|
|
|
case plcDataType.Real:
|
|
valDouble = getScaledDouble(currMem);
|
|
CurrVal = ModbusClient.ConvertFloatToRegisters((float)valDouble, ModbusClient.RegisterOrder.HighLow);
|
|
fatto = writeInputReg(currMem.index, CurrVal);
|
|
break;
|
|
|
|
//case plcDataType.String:
|
|
// // se ho writePre --> "allungo" di 2 la dimensione della stringa x MemBlock...
|
|
// if (writePre)
|
|
// {
|
|
// MemBlock = new byte[byteSize + 2];
|
|
// }
|
|
// saveStringOnMemBlock(ref MemBlock, 0, currMem.size, currMem.value);
|
|
// break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
lgInfo($"---------------MemBlock data---------------{Environment.NewLine}CurrVal: {CurrVal}{Environment.NewLine}--------------- END data ---------------");
|
|
if (!string.IsNullOrEmpty(memAddrWrite))
|
|
{
|
|
// se fatto --> aggiorno!
|
|
if (fatto)
|
|
{
|
|
item.value = item.reqValue;
|
|
item.reqValue = "";
|
|
item.lastRead = DateTime.Now;
|
|
item.UM = currMem.unit;
|
|
}
|
|
#if false
|
|
// se configurato faccio verifica write...
|
|
if (getOptPar("WRITE_CHECK") == "TRUE")
|
|
{
|
|
byte[] MemBlockRead = new byte[MemBlock.Length];
|
|
S7ReadBB(ref MemBlockRead, memAddrWrite, MemBlock.Length);
|
|
// se non corrispondessero loggo!
|
|
if (!MemBlock.SequenceEqual(MemBlockRead))
|
|
{
|
|
lgError($"Errore: mancata corrispondenza tra dati scritti e letti:{Environment.NewLine}Write: {BitConverter.ToString(MemBlock)}{Environment.NewLine}read: {BitConverter.ToString(MemBlockRead)}");
|
|
}
|
|
else
|
|
{
|
|
lgInfo($"Scrittura corretta: {BitConverter.ToString(MemBlockRead)}");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
lgInfo($"Errore: memAddrWrite vuoto!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgInfo($"Errore uid non trovato in area write memory: {item.uid}, ci sono {memMap.mMapWrite.Count} in area write");
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Eccezione in fase di plcWriteParams per item {item.uid} con valore {item.value}{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Imposto parametri PLC
|
|
/// </summary>
|
|
protected override void setParamPlc()
|
|
{
|
|
// Creo oggetto connessione NC
|
|
parentForm.commPlcActive = true;
|
|
lgInfo($"Start init Adapter ModBus TCP all'IP {cIobConf.cncIpAddr} | port: {cIobConf.cncPort} | --> IOB {cIobConf.codIOB}");
|
|
// SE è necessario refresh...
|
|
if (needRefresh)
|
|
{
|
|
lgInfo("Refreshing connection...");
|
|
if (parametri != null)
|
|
{
|
|
try
|
|
{
|
|
parametri.ipAdrr = cIobConf.cncIpAddr;
|
|
parametri.port = int.Parse(cIobConf.cncPort);
|
|
// leggo file init...
|
|
lgInfo("Reading ini file...");
|
|
IniFile fIni = new IniFile(cIobConf.iniFileName);
|
|
// ora leggo valori speciali
|
|
parametri.memAddrRead = fIni.ReadString("MEMORY", "ADDR_READ", "");
|
|
parametri.memAddrWrite = fIni.ReadString("MEMORY", "ADDR_WRITE", "");
|
|
parametri.memSizeRead = fIni.ReadInteger("MEMORY", "SIZE_READ", 0);
|
|
parametri.memSizeWrite = fIni.ReadInteger("MEMORY", "SIZE_WRITE", 0);
|
|
// salvo vettori memoria...
|
|
lgInfo("Set RawInput dimensions...");
|
|
RawInput = new byte[parametri.memSizeRead];
|
|
RawOutput = new byte[parametri.memSizeWrite];
|
|
// salvo parametri conn!
|
|
lgInfo(string.Format("Parametri memoria: memAddrRead: {0} | memAddrWrite: {1} | memSizeRead: {2} | memSizeWrite: {3}", parametri.memAddrRead, parametri.memAddrWrite, parametri.memSizeRead, parametri.memSizeWrite));
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError(exc, "Errore in parse parametri da IOBConf");
|
|
}
|
|
|
|
// carico conf vettore memoria...
|
|
loadMemConf();
|
|
// avvio conf blocchi memoria
|
|
setupMemBlocks();
|
|
// aggiungo DELTA x calcolo min/MAX...
|
|
string deltaValStr = getOptPar("DELTA_VAL");
|
|
if (!string.IsNullOrEmpty(deltaValStr))
|
|
{
|
|
double.TryParse(deltaValStr, out deltaVal);
|
|
}
|
|
bool enableByApp = utils.CRB("enableContapezzi");
|
|
bool enableByIob = (getOptPar("ENABLE_PZCOUNT") == "TRUE");
|
|
bool disableByIob = (getOptPar("DISABLE_PZCOUNT") == "TRUE");
|
|
if ((enableByApp || enableByIob) && !(disableByIob))
|
|
{
|
|
lgInfo("ModBus TCP: inizio gestione contapezzi");
|
|
try
|
|
{
|
|
// verifico quale modalità sia richiesta: STD (6711) oppure BIT (Custom, con indicazione area)
|
|
if (cIobConf.optPar.Count > 0 && !string.IsNullOrWhiteSpace(getOptPar("PZCOUNT_MODE")))
|
|
{
|
|
if (getOptPar("PZCOUNT_MODE").StartsWith("STD"))
|
|
{
|
|
lgInfo("Init contapezzi ModBusTCP: pzCntReload(true)");
|
|
pzCntReload(true);
|
|
// refresh associazione Macchina - IOB
|
|
sendM2IOB();
|
|
// per adesso imposto lettura PLC == contapezzi (poi farà vera lettura...)
|
|
contapezziPLC = contapezziIOB;
|
|
}
|
|
else
|
|
{
|
|
contapezziIOB = 0;
|
|
lgInfo("Contapezzi STD disabilitato: modalità {0}", getOptPar("PZCOUNT_MODE"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
contapezziIOB = 0;
|
|
lgInfo("Parametro mancante PZCOUNT_MODE");
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError(exc, "Errore in contapezzi ModBusTCP");
|
|
}
|
|
}
|
|
|
|
// ora tento avvio PLC... SE PING OK...
|
|
IPStatus esitoPing = testPingMachine;
|
|
if (esitoPing == IPStatus.Success)
|
|
{
|
|
needRefresh = false;
|
|
try
|
|
{
|
|
//Ip-Address and Port of Modbus-TCP-Server
|
|
currPLC = new ModbusClient(parametri.ipAdrr, parametri.port);
|
|
|
|
currPLC.ConnectedChanged += CurrPLC_ConnectedChanged;
|
|
|
|
// disconnetto e connetto...
|
|
if (isVerboseLog)
|
|
{
|
|
lgInfo("ModBus TCP: tryDisconnect");
|
|
}
|
|
tryDisconnect();
|
|
|
|
// lo ripeto x evitare che ci sia un loop... e tryConnect richiami la procedura corrente...
|
|
needRefresh = false;
|
|
lgInfo("ModBus TCP: tryConnect");
|
|
tryConnect();
|
|
lgInfo("End init Adapter ModBusTCP");
|
|
if (isVerboseLog)
|
|
{
|
|
lgInfo("ModBus TCP CONNESSIONE AVVENUTA");
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError(exc, "Errore in INIT ModBusTCP");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgError($"Errore in ping: esito {esitoPing}");
|
|
}
|
|
parentForm.commPlcActive = false;
|
|
}
|
|
else
|
|
{
|
|
lgError("Parametri null!");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// effettua il setup dei memblock da gestire (NON leggo intera memoria ma tanti blocchi...)
|
|
/// </summary>
|
|
protected void setupMemBlocks()
|
|
{
|
|
// se configurato -_> deserializzo
|
|
string confFile = getOptPar("MEM_BLOCK");
|
|
if (!string.IsNullOrEmpty(confFile))
|
|
{
|
|
string jsonFileName = $"{Application.StartupPath}/DATA/CONF/{confFile}";
|
|
lgInfo($"Apertura file {jsonFileName}");
|
|
using (StreamReader reader = new StreamReader(jsonFileName))
|
|
{
|
|
string jsonData = reader.ReadToEnd();
|
|
if (!string.IsNullOrEmpty(jsonData))
|
|
{
|
|
lgInfo($"File json MemBlock composto da {jsonData.Length} caratteri");
|
|
try
|
|
{
|
|
var currMem = JsonConvert.DeserializeObject<MemBlockConf>(jsonData);
|
|
memSetR = currMem.ReadBlocks;
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Errore in setupMemBlock{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test connessione CNC
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected bool testCncConn()
|
|
{
|
|
bool answ = currPLC.Connected;
|
|
if (!answ)
|
|
{
|
|
// riduco i controlli ping.. li faccio solo ogni 5 ping period se precedente positivo...
|
|
DateTime adesso = DateTime.Now;
|
|
if (lastPingOk && adesso.Subtract(lastPingConn).TotalMilliseconds < 5 * parametri.pingMsTimeout)
|
|
{
|
|
answ = lastPingOk;
|
|
}
|
|
else
|
|
{
|
|
IPStatus pingStatus = testPingMachine;
|
|
|
|
// se non ok riprovo 1 volta dopo attesa
|
|
if (pingStatus != IPStatus.Success)
|
|
{
|
|
Thread.Sleep(2 * cIobConf.pingMsTimeout);
|
|
pingStatus = testPingMachine;
|
|
}
|
|
// se non passa ancora errore!
|
|
if (pingStatus != IPStatus.Success)
|
|
{
|
|
lgError($"Errore in testCncConn | reply Status {pingStatus} | IP: {parametri.ipAdrr} | T.Out: {parametri.pingMsTimeout}ms");
|
|
}
|
|
// se passa il ping faccio il resto...
|
|
else
|
|
{
|
|
if (!currPLC.Connected)
|
|
{
|
|
currPLC.Connect();
|
|
}
|
|
|
|
if (!currPLC.Available(500))
|
|
{
|
|
lgError($"PLC ModBus NON disponibile: {currPLC.IPAddress} | {currPLC.Port}");
|
|
}
|
|
else
|
|
{
|
|
if (!currPLC.Connected)
|
|
{
|
|
lgError($"PLC ModBus NON connesso:{currPLC.IPAddress} | {currPLC.Port}");
|
|
}
|
|
else
|
|
{
|
|
// tutto ok
|
|
parentForm.updateComStats("Connection OK");
|
|
answ = true;
|
|
}
|
|
}
|
|
}
|
|
// salvo stato ping
|
|
lastPingConn = adesso;
|
|
}
|
|
lastPingOk = answ;
|
|
}
|
|
|
|
return answ;
|
|
}
|
|
|
|
#endregion Protected Methods
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Metodo dispose x il currPLC contenuto
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
currPLC.Disconnect();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Processo i task richiesti e li elimino dalla coda 1:1
|
|
/// </summary>
|
|
/// <param name="task2exe"></param>
|
|
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
|
|
{
|
|
lgInfo($"Chiamata executeTasks specifica ModBus TCP: {task2exe.Count} task ricevuti");
|
|
// Verificare il protocollo: dovrebeb togliere SOLO i task eseguiti...
|
|
Dictionary<string, string> taskDone = new Dictionary<string, string>();
|
|
string taskVal = "";
|
|
if (task2exe != null)
|
|
{
|
|
// cerco task specifici
|
|
foreach (var item in task2exe)
|
|
{
|
|
taskVal = "";
|
|
// converto richiesta in enum...
|
|
taskType tName = taskType.nihil;
|
|
Enum.TryParse(item.Key, out tName);
|
|
// controllo sulla KEY
|
|
switch (tName)
|
|
{
|
|
case taskType.nihil:
|
|
case taskType.fixStopSetup:
|
|
case taskType.forceResetPzCount:
|
|
case taskType.forceSetPzCount:
|
|
case taskType.setProg:
|
|
case taskType.sendWatchDogMes2Plc:
|
|
case taskType.startSetup:
|
|
case taskType.stopSetup:
|
|
case taskType.setPzComm:
|
|
case taskType.setArt:
|
|
case taskType.setComm:
|
|
taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC";
|
|
break;
|
|
|
|
case taskType.setParameter:
|
|
// richiedo da URL i parametri WRITE da popolare
|
|
lgInfo("Chiamata processMemWriteRequests");
|
|
|
|
taskVal = processMemWriteRequests();
|
|
// se restituiscce "" faccio altra prova...
|
|
if (string.IsNullOrEmpty(taskVal))
|
|
{
|
|
// i parametri me li aspetto come stringa composta paramName|paramvalue
|
|
if (item.Value.Contains("|"))
|
|
{
|
|
string[] paramsJob = item.Value.Split('|');
|
|
taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}";
|
|
}
|
|
else
|
|
{
|
|
taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value";
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
taskVal = "SKIPPED | NO EXEC";
|
|
break;
|
|
}
|
|
// aggiungo task!
|
|
taskDone.Add(item.Key, taskVal);
|
|
}
|
|
}
|
|
return taskDone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decodifica tipo indirizzo dal codice
|
|
/// vedere ad esempio https://www.fernhillsoftware.com/help/drivers/modbus/data-address-format.html
|
|
/// </summary>
|
|
/// <param name="memAddr"></param>
|
|
/// <returns></returns>
|
|
public modBusAddrType getAddrType(string memAddr)
|
|
{
|
|
modBusAddrType answ = modBusAddrType.Coil;
|
|
// leggo prima cifra...
|
|
answ = (modBusAddrType)Enum.Parse(typeof(modBusAddrType), memAddr.Substring(0, 1));
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recupero dati dinamici...
|
|
/// ATTENZIONE factor usato come FONDOSCALA
|
|
///
|
|
/// Esempio:
|
|
/// - RealVal : 0 - 28000
|
|
/// - ReadVal : 0 - 100
|
|
/// - Factor : 28000 / 100 = 280
|
|
/// </summary>
|
|
public override Dictionary<string, string> getDynData()
|
|
{
|
|
// valore non presente in vers default... se gestito fare override
|
|
Dictionary<string, string> outVal = new Dictionary<string, string>();
|
|
if (utils.CRB("enableTSVC"))
|
|
{
|
|
// processing SOLO SE ho in memoria abbastanza dati...
|
|
if (RawInput.Length < parametri.memSizeRead)
|
|
{
|
|
lgError($"Impossibile processare getDynData x ModBus TCP PLC, vettore memoria troppo piccolo: {RawInput.Length} byte / {parametri.memSizeRead} byte presenti/richiesti)");
|
|
}
|
|
else
|
|
{
|
|
Dictionary<string, dataConfTSVC> readErrorList = new Dictionary<string, dataConfTSVC>();
|
|
Dictionary<string, dataConfTSVC> readErrorListRepeat = new Dictionary<string, dataConfTSVC>();
|
|
bool useLUT = memSetR != null && memSetR.Count > 0;
|
|
// se configurato leggo IN BLOCCHI da memoria...
|
|
if (useLUT)
|
|
{
|
|
foreach (var item in memSetR)
|
|
{
|
|
readBlockHoldingReg(item.Key, item.Value);
|
|
Thread.Sleep(100);
|
|
}
|
|
}
|
|
// procedo ...
|
|
try
|
|
{
|
|
// processo x ogni valore configurato...
|
|
if (memMap.mMapRead.Count > 0)
|
|
{
|
|
var memItemList = memMap.mMapRead;
|
|
|
|
readErrorList = getDataDictionary(memItemList, useLUT, ref outVal);
|
|
// se qualcosa è andato storto riprovo a caricare SOLO gli errori... 1 sola volta
|
|
if (readErrorList.Count > 0)
|
|
{
|
|
lgInfo($"Attesa prima di rilettura | LUT: {useLUT}");
|
|
// attendo 3 sec
|
|
Thread.Sleep(3000);
|
|
lgInfo($"Effettuo rilettura per {readErrorList.Count} variabili | LUT: {useLUT}");
|
|
readErrorListRepeat = getDataDictionary(readErrorList, useLUT, ref outVal);
|
|
}
|
|
// se avessi ancora errori --> disconnetto
|
|
if (readErrorListRepeat.Count > 0)
|
|
{
|
|
lgInfo($"Trovati valori non validi al secondo tentativo | LUT: {useLUT} --> invalido valori letti e resetto adapter con tryDisconnect!");
|
|
tryDisconnect();
|
|
// invalido output
|
|
outVal = new Dictionary<string, string>();
|
|
tryConnect();
|
|
}
|
|
else
|
|
{
|
|
lastReadPLC = DateTime.Now;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgInfo($"getDynData: {memMap.mMapRead.Count} record in mMapRead | LUT: {useLUT}");
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError(exc, "Errore in getDynData x ModBus TCP PLC --> ciclo disconnect/reconnect | LUT: {useLUT}");
|
|
tryDisconnect();
|
|
outVal = new Dictionary<string, string>();
|
|
tryConnect();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgInfo($"Non processo getDynData: enableTSVC = false");
|
|
}
|
|
if (periodicLog || outVal.Count > 0)
|
|
{
|
|
lgInfo($"Esito getDynData: {outVal.Count} valori VALIDI in outVal");
|
|
}
|
|
return outVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lettura valori Coils (1...)
|
|
/// </summary>
|
|
/// <param name="startAddr"></param>
|
|
/// <param name="qty"></param>
|
|
/// <returns></returns>
|
|
public bool[] readCoil(int startAddr, int qty)
|
|
{
|
|
bool[] answ = new bool[1];
|
|
if (currPLC.Connected)
|
|
{
|
|
answ = currPLC.ReadCoils(startAddr, qty);
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lettura valori DiscreteInputs (2...)
|
|
/// </summary>
|
|
/// <param name="startAddr"></param>
|
|
/// <param name="qty"></param>
|
|
/// <returns></returns>
|
|
public bool[] readDiscrInputs(int startAddr, int qty)
|
|
{
|
|
bool[] answ = new bool[1];
|
|
if (currPLC.Connected)
|
|
{
|
|
answ = currPLC.ReadDiscreteInputs(startAddr, qty);
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lettura valori Holding Register (3...)
|
|
/// </summary>
|
|
/// <param name="startAddr"></param>
|
|
/// <param name="qty"></param>
|
|
/// <returns></returns>
|
|
public int[] readHoldReg(int startAddr, int qty)
|
|
{
|
|
int[] answ = new int[2];
|
|
if (currPLC.Connected)
|
|
{
|
|
answ = currPLC.ReadHoldingRegisters(startAddr, qty);
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lettura valori Input Register (4...)
|
|
/// </summary>
|
|
/// <param name="startAddr"></param>
|
|
/// <param name="qty"></param>
|
|
/// <returns></returns>
|
|
public int[] readInputReg(int startAddr, int qty)
|
|
{
|
|
int[] answ = new int[2];
|
|
try
|
|
{
|
|
if (currPLC.Connected)
|
|
{
|
|
answ = currPLC.ReadInputRegisters(startAddr, qty);
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgError($"Errore in readInputReg{Environment.NewLine}{exc}");
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua lettura semafori principale
|
|
/// <paramref name="currDispData">Parametri da aggiornare x display in form</paramref>
|
|
/// </summary>
|
|
public override void readSemafori(ref newDisplayData currDispData)
|
|
{
|
|
// NON eseguo x NON indicare data ora ultima lettura se NON fatta davvero
|
|
//base.readSemafori(ref currDispData);
|
|
byte[] MemBlock = new byte[2];
|
|
try
|
|
{
|
|
currDispData.semIn = Semaforo.SV;
|
|
|
|
if (verboseLog)
|
|
{
|
|
lgInfo("inizio read semafori");
|
|
}
|
|
|
|
if (verboseLog)
|
|
{
|
|
lgInfo(string.Format("RawInput[0]: {0}", utils.binaryForm(RawInput[0])));
|
|
}
|
|
|
|
// salvo il solo BYTE dell'input decifrando il semaforo...
|
|
decodeToBaseBitmap();
|
|
decodeOtherData();
|
|
// riporto bitmap...
|
|
reportRawInput(ref currDispData);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
currDispData.semIn = Semaforo.SR;
|
|
lgError($"Eccezione in readSemafori{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Effettua salvataggio in LUT del valore ricevuto (double)
|
|
/// </summary>
|
|
/// <param name="outVal"></param>
|
|
/// <param name="valore"></param>
|
|
/// <param name="chiave"></param>
|
|
/// <returns></returns>
|
|
public override void saveValue(ref Dictionary<string, string> outVal, double valore, string chiave)
|
|
{
|
|
//check obj preliminare
|
|
if (outVal == null)
|
|
{
|
|
outVal = new Dictionary<string, string>();
|
|
}
|
|
bool scaduto = stackVal_TSVC(chiave, valore);
|
|
// recupero VC
|
|
valore = getVal_TSVC(chiave, scaduto);
|
|
if (scaduto)
|
|
{
|
|
outVal.Add(chiave, $"{valore:N3}");
|
|
}
|
|
LastTSVC[chiave] = valore;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override connessione
|
|
/// </summary>
|
|
public override void tryConnect()
|
|
{
|
|
bool doLog = (verboseLog || periodicLog);
|
|
lgInfo("ModBus TCP: tryConnect step 01");
|
|
if (!connectionOk)
|
|
{
|
|
// SE è necessario refresh...
|
|
if (needRefresh)
|
|
{
|
|
lgInfo("ModBus TCP: tryConnect step 02");
|
|
|
|
// reimporto parametri PLC se necessario...
|
|
setParamPlc();
|
|
}
|
|
lgInfo("ModBus TCP: tryConnect step 03");
|
|
|
|
// controllo che il ping sia stato tentato almeno pingTestSec fa...
|
|
if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
|
|
{
|
|
if (doLog)
|
|
{
|
|
lgInfo("ModBus TCP: ConnKO - tryConnect");
|
|
}
|
|
lgInfo("ModBus TCP: tryConnect step 04");
|
|
|
|
// in primis salvo data ping...
|
|
lastPING = DateTime.Now;
|
|
// se passa il ping faccio il resto...
|
|
if (testPingMachine == IPStatus.Success)
|
|
{
|
|
string szStatusConnection = "ND";
|
|
try
|
|
{
|
|
// ora provo connessione...
|
|
parentForm.commPlcActive = true;
|
|
currPLC.Connect();
|
|
szStatusConnection = "OPEN";
|
|
parentForm.commPlcActive = false;
|
|
connectionOk = currPLC.Connected;
|
|
lgInfo($"StatusConnection: {szStatusConnection}");
|
|
// refresh stato allarmi!!!
|
|
if (connectionOk)
|
|
{
|
|
if (adpRunning)
|
|
{
|
|
lgInfo($"Connessione OK: {connectionOk} | adpRunning: {adpRunning}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lgError("Impossibile procedere, connessione mancante...");
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgFatal($"Errore in TryConnect adapter ModBusTCP | szStatusConnection {szStatusConnection}{Environment.NewLine}{exc}");
|
|
connectionOk = false;
|
|
needRefresh = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// loggo no risposta ping ...
|
|
connectionOk = false;
|
|
if (doLog)
|
|
{
|
|
lgInfo($"Attenzione: ModBusTCP controllo PING fallito per IP {cIobConf.cncIpAddr}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se non è ancora connesso faccio procesisng memoria caso disconnesso...
|
|
if (!connectionOk)
|
|
{
|
|
// processo semafori ed invio...
|
|
processMemoryDiscon();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override disconnessione
|
|
/// </summary>
|
|
public override void tryDisconnect()
|
|
{
|
|
lgInfo("Richiesta disconnessione adapter ModBusTCP!");
|
|
try
|
|
{
|
|
if (currPLC.Connected)
|
|
{
|
|
currPLC.Disconnect();
|
|
}
|
|
lgInfo("Effettuata disconnessione adapter ModBusTCP!");
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
lgFatal($"Errore nella disconnessione dall'adapter ModBusTCP{Environment.NewLine}{exc}");
|
|
}
|
|
connectionOk = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scrittura di un singolo valore COIL (1...)
|
|
/// </summary>
|
|
/// <param name="startAddr"></param>
|
|
/// <param name="currValue"></param>
|
|
/// <returns></returns>
|
|
public bool writeCoil(int startAddr, bool currValue)
|
|
{
|
|
bool answ = false;
|
|
if (currPLC.Connected)
|
|
{
|
|
try
|
|
{
|
|
currPLC.WriteSingleCoil(startAddr, currValue);
|
|
answ = true;
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scrittura di un valore Input Register (4...)
|
|
/// </summary>
|
|
/// <param name="startAddr"></param>
|
|
/// <param name="currRegVal">Valore in formato INT da registri</param>
|
|
/// <returns></returns>
|
|
public bool writeInputReg(int startAddr, int[] currRegVal)
|
|
{
|
|
bool answ = false;
|
|
if (currPLC.Connected)
|
|
{
|
|
try
|
|
{
|
|
currPLC.WriteMultipleRegisters(startAddr, currRegVal);
|
|
answ = true;
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
#endregion Public Methods
|
|
}
|
|
} |