Come dissi, non conosco Synapse, ma le caratteristiche di un socket sono comuni a tutti i componenti.
Sia i client TCP che i server TCP (come anche gli UDP) hanno la possibilità di sapere da che socket remoto (IP e Porta) arrivano i dati, quindi anche con un solo socket puoi tranquillamente rilevare i dati, perchè interrogando le sue proprietà puoi identificare la fonte.
Anche la velocità non è un problema (con un solo socket riesco a gestire flussi non compressi di decine di immagini da decine di megabyte).
Poi dipende dalla logica del tuo programma.
Ti faccio un esempio banale con Indy:
//Evento ricezione dati da socket UDP comune a tutte le schede remote
procedure TCardComm.fIdUDPServer1UDPRead(AThread: TIdUDPListenerThread;
const AData: TIdBytes; ABinding: TIdSocketHandle);
var z: cardinal;
fLastPeerIP: string;
//fLastPeerPort: word;
fLastData: TIdBytes;
begin
//incrementa un contatore interno per diagnostica
InterlockedIncrement64(fContatore);
try
//Rileva l'IP Remoto di connessione
fLastPeerIP := ABinding.PeerIP;
//Rileva la PORTA Remota (non è di interesse)
//fLastPeerPort := ABinding.PeerPort;
//Legge i dati ricevuti (e automaticamente svuota il buffer di ricezione)
fLastData := AData;
z := 0; //Tiene conto della corsia (un sensore per corsia)
{$IFDEF DEBUG}
if fLastPeerIP = '127.0.0.1' then
begin
z := 0;
end
else
{$ENDIF}
if fLastPeerIP = '192.168.137.11' then
begin
z := 0;
end
else
if fLastPeerIP = '192.168.137.12' then
begin
z := 1;
end
......
Assegna i dati (FLastData) a qualche contenitore indicizzato da Z ed elabora, oppure li usi negli altri Thread
Ciao
Ciao, io come te uso dispositivi di campo che usano sia seriali 485 (a 1 Mbit) che modbus TCP (senza convertitori).
La logica dei Thread mi pare corretta, e se usi i componenti corretti non dovresti avere "buchi" sulle prestazioni.
Per quello che riguarda il modbus TCP (sia esso diretto che tramite convertitori con finali RTU), l'importante e che usi socket non bloccanti, oppure in alternativa usi un componente (per esteso un socket) per ogni dispositivo.
Su rete Ethernet da 100 Mbit, purtroppo i dispositivi industriali sono ancora alla Fast Ethernet, io interrogando via modbus TCP una periferica modbus (in questo caso PLC con modbus TCP hardware integrato) ho tempi di risposte (inteso tra l'invio comando e la ricezione risposta) intorno ai 2 o 3 millisecondi. Con altri dispositivi, dove ci sono convertitori di mezzo o non sono modbus hardware, difficilmente riesco a raggiungere velocità inferiori ai 80 ms. (in modo costante, diciamo facendo i test con 1000 invii / ricezioni).
In TCP il numero di dati che scambi è ininfluente sulla velocità, in RS485 (o RS232) il tempo di scambio dipende dal numero di dati che scambi e dalla velocità ... se hai una rete a 100 Mbit, ma la seriale del dispositivo è a 38400 baud (cioè 0,0384 Mbit) .... la differenza è decisamente apprezzabile.
Se vuoi, monitora i tempi con l'unità di diagnostica che allego (se non esiste già nell'ultima versione di Lazarus):
//Uso, aggiungi l'unità tra le Uses della tua Unità
Uses Diagnostics;
//Puoi dichiarare quanti "timer" vuoi, anche come membri di classe, locali, ....
var HighTimer: TStopWatch;
.....
begin
HighTimer := TStopWatch.StartNew;
...... //funzione da eseguire
HighTimer.Stop;
ShowMessage(fHighTimer.ElapsedMilliseconds.ToString); //In realtà puoi spingerti a misurare multipli di 100 nanosecondi
//Per eseguire un'altra misura
HighTimer.Reset;
HighTimer.Start;
...... //funzione da eseguire
HighTimer.Stop;
ShowMessage(fHighTimer.ElapsedMilliseconds.ToString);
end;
In questo modo puoi monitorare i tempi di ogni comunicazione e verificare dove stà il colle di bottiglia.
Puoi anche crearti un'array di timer, in modo da indicizzare tutte le tempistiche in un colpo solo (occhio al threading ovviamente, consiglio di utilizzare solo timer dichiarati nella propria unità).
Se vuoi, qui c'è un codice che puoi usare come partenza ..... Semplicemente Crea una applicazione, poi inserisci un pulsante nella form, doppio click sia sul pulsante che sulla form e poi copia tutto il codice qui elencato (o copia a pezzi, come ritieni più opportuno).
unit Unit1;
{$mode objfpc}{$H+}
{$modeswitch advancedrecords}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
const
MAX_TAPPARELLE = 13;
type
TInput=record
IndxDb,IndxArr,Numero,Timer,AnValoreAttuale,AnValMax,AnValMin,AnValIntervento,AnValCampionatura,
IndxArrDispositivo,Utilizzo:Integer; //utilizzo corrisponde al'indice della corrispondente tabella nel db
Etichetta,IcoON,IcoOFF,Suono,IPDispositivo:String;
Invertito,Antifurto,Digitale:Boolean;
IndxOutComandato:array of Integer; //inserire il numero del/degli output da comandare che corrisponde all'indice dell'array
Stato: TStato;
end;
type
TDati_Tap = record
strict private ArrInput:array of TInput;
function getArrayInput(Index: cardinal): TInput;
procedure setArrayInput( Index: cardinal; Value: TInput);
private
class operator Initialize (var Dest: TDati_Tap);
class operator Finalize (var Dest: TDati_Tap);
public
property Tappa[Index: cardinal]: TInput read getArrayInput write setArrayInput;
end;
type
EDATITAPPARELLEINVALID = class(Exception);
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
public
end;
var
Form1: TForm1;
Tapparelle: TDati_Tap;
implementation
{$R *.lfm}
{ TForm1 }
class operator TDati_Tap.Initialize (var Dest: TDati_Tap);
var i: cardinal;
begin
SetLength(Dest.ArrInput, MAX_TAPPARELLE);
//Qui puoi inizializzare tutti i valori dell'array
for i:= Low(Dest.ArrInput) to High(Dest.ArrInput) do
begin
Dest.ArrInput[i].IndxDb:= 0;
//etc .....
end;
end;
class operator TDati_Tap.Finalize (var Dest: TDati_Tap);
begin
SetLength(Dest.ArrInput, 0);
end;
function TDati_Tap.getArrayInput(Index: cardinal): TInput;
begin
if (Index >= Low(ArrInput)) and (Index <= High(ArrInput)) then
begin
Result := ArrInput[Index];
end
else
raise EDATITAPPARELLEINVALID.Create('Richiesta al di fuori dei limiti');
end;
procedure TDati_Tap.setArrayInput(Index: cardinal; Value: TInput);
begin
if (Index >= Low(ArrInput)) and (Index < High(ArrInput)) then
begin
ArrInput[Index] := Value;
end
else
raise EDATITAPPARELLEINVALID.Create('Richiesta al di fuori dei limiti');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(Tapparelle.Tappa[0].IndxDb.toString);
end;
procedure TForm1.FormCreate(Sender: TObject);
var prova: TInput;
begin
Prova.IndxDb := 20;
//Prova.Antifurto:= ....;
//etc ....
Tapparelle.Tappa[0] := Prova;
end;
end.
P.S.: Non ti ho inserito barriere di protezione, è solo per darti un là su un altro modo di gestire i dati.
Ciao