OVH Cloud OVH Cloud

Controle d'un modem RTC en java

6 réponses
Avatar
Plantex
Bonjour,

J'aimerais savoir s'il existe un library de base pour contr=F4ler un
modem RTC de type Olitec V90/V92 en lui envoyant des commandes modem
classiques. Ou peut-=EAtre y a t il un exemple d'une appli java qui fait
fonctionner un modem pour =E9mettre des appels ?

Merci d'avance

6 réponses

Avatar
alexandre cartapanis
Plantex wrote:
Bonjour,

J'aimerais savoir s'il existe un library de base pour contrôler un
modem RTC de type Olitec V90/V92 en lui envoyant des commandes modem
classiques. Ou peut-être y a t il un exemple d'une appli java qui fai t
fonctionner un modem pour émettre des appels ?

Merci d'avance

Ca s'appel javacomm, et ca se trouve ici

http://java.sun.com/products/javacomm/
C'est une API de bas niveau, il te faudrat gerer toi meme les signaux,
mais c'est assez puissant je crois.

--
Alexandre CARTAPANIS - Responsable Système et Réseau
Email
Gsm. 06 72 07 51 55

Macymed SARL - 9 bvd Kraëmer 13014 Marseille France
Tél. 04 91 48 31 58 - Fax. 04 91 02 36 47
Web http://www.macymed.fr - Email

Avatar
Fabien Bergeret
alexandre cartapanis wrote:
Plantex wrote:

Bonjour,

J'aimerais savoir s'il existe un library de base pour contrôler un
modem RTC de type Olitec V90/V92 en lui envoyant des commandes modem
classiques. Ou peut-être y a t il un exemple d'une appli java qui fait
fonctionner un modem pour émettre des appels ?

Merci d'avance

Ca s'appel javacomm, et ca se trouve ici

http://java.sun.com/products/javacomm/
C'est une API de bas niveau, il te faudrat gerer toi meme les signaux,
mais c'est assez puissant je crois.

Oui, je confirme.

J'ai eu l'occasion de piloter une batterie de modems depuis Java.
javacomm permet de lire/ecrire sur les ports serie (on parametre un
port, on l'ouvre, puis on recupere un InputStream et un OutputStream
pour lire/ecrire, ou on s'abonne a des SerialPortEvent (de memoire) pour
etre prevenu quand des donnees sont disponibles), et on oublie pas de
fermer le port.
Un modem dialogue avec une appli par ce que l'on appelle les commandes AT

exemple : ATDT 1234567890 pour appeler le numero 1234567890


Avatar
pcouas
Il existe une API payante qui fait cela au desssus de JavaCOMM
http://www.java4less.com/java_fax.htm
A+
Avatar
Plantex
Alors, si tu as fait ça, aurais tu un bout de code simple pour amorcer
l'apprentissage et la prise en main ?
Cette api est-elle téléchargeable pour Windows?
D'ailleurs, je ne sais pas trop comment ajouter un jar à eclipse pour
qu'il me prenne javax.comm....

Merci
Avatar
Fabien Bergeret
Plantex wrote:
Alors, si tu as fait ça, aurais tu un bout de code simple pour amorcer
l'apprentissage et la prise en main ?
Cette api est-elle téléchargeable pour Windows?
D'ailleurs, je ne sais pas trop comment ajouter un jar à eclipse pour
qu'il me prenne javax.comm....

Merci

Oui, meme si la version 3.0 n'est pas supporter pour Windows, la 2.0

traine toujours sur le net :
http://www.dadid.com/tmp/javacomm20-win32.zip
J'ai pas de code sous la main, mais il existe un tutoriel :
http://christophej.developpez.com/tutoriel/java/javacomm/
Pour ajouter un jar a Eclipse : clic droit sur le projet, build-path,
ajouter un jar externe, selectionner le jar en question, et hop

Avatar
Fabien Bergeret
Plantex wrote:
Alors, si tu as fait ça, aurais tu un bout de code simple pour amorcer
l'apprentissage et la prise en main ?
Cette api est-elle téléchargeable pour Windows?
D'ailleurs, je ne sais pas trop comment ajouter un jar à eclipse pour
qu'il me prenne javax.comm....

Merci

Voici une classe de gestion de modem.

Attention, certaines commandes AT sont specifiques au modem utilise.
Enjoy :

package javapad.liaison;
import java.io.*;
import java.util.*;
import javax.comm.*;
import org.apache.log4j.*;
import javapad.util.*;
import javapad.reseau.*;
import javapad.*;
/**
* Classe utilisee pour communiquer avec un modem par lien serie, de
facon a recuperer les trames
* que les TPE envoient par modem pour les renvoyer vers des
LiaisonListener.
* @see LiaisonListener
* @version 3.0
*/

public class LiaisonSerie extends Liaison implements SerialPortEventListener
{
/**
* Retourne les parametres de la liaison serie.
* Tous les parametres sont des Integer. Le premier contient le debit
du port serie, le second le nombre de bits des donnees, le troisieme le
nombre de bits de stops et le quatrieme, la parite.
*/
public Object [] getParams()
{
Object [] retour = { new Integer(sp.getBaudRate()),new
Integer(sp.getDataBits()),new Integer(sp.getStopBits()),new
Integer(sp.getParity())};
return retour;
}
/**
* Change les parametres du port serie.
* @see #getParams
*/
public void setParams(Object [] params) throws LiaisonException
{
try
{
int baud = ((Integer)(params[0])).intValue();
int db = ((Integer)(params[1])).intValue();
int sb = ((Integer)(params[2])).intValue();
int parity = ((Integer)(params[3])).intValue();
sp.setSerialPortParams(baud,db,sb,parity);
}
catch(UnsupportedCommOperationException e)
{
throw new LiaisonException(e.getMessage());
}
}
/**
* Utilise a des fin de tests uniquement
*/
public static void main(String args[]) throws Exception
{
LiaisonSerie ls = new LiaisonSerie("COM3");
ls.litDonnees();
ls.litDonnees();
}
/**
* Constructeur prenant en parametre le nom du port serie sur lequel
on va travailler.
* Le port ne sera ouvert qu'une fois que le gestionnaire CB com aura
appele la methode
* intialiserCommunication
* @param portName le nom du port serie sur lequel la liaison seria
va travailler.
*/
public LiaisonSerie(String portName)
{
super(portName);
//listeCommande = new LinkedList();
setLog(Log.getInstance(portName));
logTransaction = Log.getTransactionInstance(portName);
config = Configuration.getInstance();
}
/**
* Se retire comme abonne des evements serie, et ferme le port serie.
* @throws LiaisonException si l'une ou l'autre de ces operations se
passe mal.
*/
public void arret() throws LiaisonException
{
try
{
super.arret();
this.arret = true;
sp.removeEventListener();
sp.close();
}
catch(Exception e)
{
throw new LiaisonException(e.getMessage());
}
}
/**
* Methode de l'interface utilisee pour demande d'arreter une
communication au niveau liaison.
* Repasse le modem en mode commande (+++), lui demande de raccrocher
(ATH), et reinitialise les donnees
* internes.
* @throws LiaisonException si l'envoi de la commande ATH de
raccrochage pose un probleme.
*/
public synchronized void arreterCommunication() throws LiaisonException
{
LiaisonException exception = null;
//Pas besoin du +++ ATH si on est deja en mode AT (cas d'une
coupure externe de la comm)
if ( !modeAT )
{
//1 dire a l'appli qu'on repasse en mode commande
modeAT = true;
//2 envoyer un +++ATH (repasse en mode commande, demande de
raccrocher)
for (int i = 0 ; i < 3; i++)
{
try
{
if ( commandeAT("+++rnATH","OK",3000) )
break;
}
catch(Exception e)
{
exception = new LiaisonException(e.getMessage());
}
}
}
//3 reset des donnnes
reliquat = null;
resteTrame = null;
//4 reinit du modem
try
{
initialisationModem();
}
catch(Exception e)
{
if ( exception == null )
exception = new LiaisonException(e.getMessage());
}
if ( exception != null )
throw exception;
}
/**
* Methode de l'interface initialisant la communication. A l'issue de
cette methode
* la LiaisonSerie est prete a recevoir des appels.
* Cette methode ouvre port serie et initialise le modem.
* @see #ouvrePort()
* @see #initialisationModem()
* @throws LiaisonException si l'une des deux operations precedent se
passe mal.
*/
public void initialiserCommunication() throws LiaisonException
{
try
{
ouvrePort();
initialisationModem();
}
catch(Exception e)
{
throw new LiaisonException(e.getMessage());
}
}
/**
* Methode de l'interface demander d'envoyer des donnees.
* Envoie les donnees sur le port serie et force l'envoi par un flush
* @param data les donnees a envoyer.
* @throws LiaisonException si l'envoi des donnees se passe mal.
*/
public void envoyerTrame(byte []data) throws LiaisonException
{
if ( !modeAT )
{
String traceLog = "Emission COM
"+BufferToString.toString(data,false);
if ( config.isTraceEmissionPortCom() || trace )
getLog().info(traceLog);
//Preparation de la journalisation des transactions foirees
logTransaction.info(traceLog);
try
{
out.write(data);
out.flush();
}
catch(Exception e)
{
throw new LiaisonException(e.getMessage());
}
}
}
/**
* Methode de l'interface SerialPortEventListener.
* Si un evenement d'arrivee de donnees est recue, lit la commande AT
si on
* est en mode AT, ou lit la trame CBcom sinon.
* @see #litAT
* @see #litDonnees
*/
public synchronized void serialEvent(SerialPortEvent event)
{
try
{
switch(event.getEventType())
{
case SerialPortEvent.DATA_AVAILABLE :
if ( config.isTraceDataAvailable() )
{
getLog().info("Donnees recues");
}
if ( modeAT )
{
litAT();
}
else
{
litDonnees();
}
break;
case SerialPortEvent.CD :
if ( config.isTraceCarrierDetect())
{
getLog().info("Porteuse detectee");
}
if ( !modeAT )
{
fireLinkCut();
modeAT = true;
}
break;
}
}
catch(Exception e)
{
JavaPad.getInstance().ajouteErreur(e.getMessage());
getLog().error("LiaisonSerie.serialEvent Erreur lecture",e);
}
//Previent les methodes en attentes que des donnees sont disponibles.
notifyAll();
}
/**
* Lit une commande AT envoyee par le modem.
* Lit toutes les donnees disponibles. Ces donnees ne correspondent
par forcement
* a une commande entiere (exemple : 0xD 0xA NO CAR puis RRIER 0xD
0xA 10 ms apres).
* Si la commande precedent n'etait pas complete, la commande a
traiter est la concatenation
* de la commande precedent et de la commande lue, sinon, c'est la
commande lue.
* On fait ensuite une recherche de rn dans la chaine et chaque
commande est ajoutee a la liste
* des commandes lues (qui est remis a 0 au debut de la methode). On
lance ensuite un interpreteur
* qui realise un certain nombre d'actions suivant ce qui est lu
(RING,CONNECT).
* On place ce qui reste des donnees a analyser dans un attribut pour
repeter l'algorithme.
* Enfin, on previent tous les threads en attente que des commandes
AT ont ete lues.
* @see Interpreteur
*/
private void litAT() throws Exception
{
getLog().debug("LiaisonSerie.litAT");
//listeCommande.clear();
int taille = in.available();
byte [] buffer = new byte[taille];
in.read(buffer);
String traceLog = "Reception COM "+BufferToString.toString(buffer);
if ( config.isTraceReceptionPortCom() || trace)
{
getLog().info(traceLog);
}
//Preparation de la journalisation des transactions foirees
logTransaction.info(traceLog);
if ( reliquat == null )
reliquat = new String(buffer);
else
reliquat += new String(buffer);
//Recherche rn
StringTokenizer st = new StringTokenizer(reliquat,da);
String commande = null;
while(st.hasMoreTokens())
{
commande = st.nextToken();
getLog().debug("LiaisonSerie.litAT : "+commande+" recu");
//listeCommande.add(commande);
if ( (aChercher != null ) && (commande.indexOf(aChercher)!=-1))
{
aChercher = null;
notify();
}
//L'interpretation des commandes recues se fait dans un autre
thread pour eviter les blocages
//Scenario rencontre : RING
//reponse ATA
//un RING arrive
//reponse ATA
//modem repond CONNECT
//et le premier ATA attend toujours son CONNECT
new Interpreteur(commande);
}
//Il existe des OK qui ne se terminent pas par 0xD, 0xA, il
convient de les traiter quand meme
/*if ( (reliquat.indexOf("OK") != -1 ) && (
listeCommande.contains("OK")))
listeCommande.add("OK");*/
//Si la chaine ne se termine pas par rn : cas d'une commande sur
plusieurs lignes
//ou qu'une trame de donnees est deja arrivee
int dernierSeparateur = reliquat.lastIndexOf(da);
if ( dernierSeparateur != reliquat.length()-2)
{
//listeCommande.remove(commande);
reliquat = commande;
resteTrame = commande.getBytes();
getLog().debug("LiaisonSerie.litAt Commande non complete, reste
"+reliquat+" "+BufferToString.toString(resteTrame));
}
else
{
reliquat = null;
resteTrame = null;
}
notifyAll();
}
/**
* Methode appelee par serialEvent pour lire des donnees sur la
liaison serie et les envoyer aux objets interesses.
* Lit toutes les donnees disponibles sur la ligne serie. Si les
donnees contiennent "NO CARRIER", previent les objets
* a notifier que le lien est rompu. Envoie les donnees aux objets a
notifier.
* @see #rechercheNoCarrier(byte[])
* @see Liaison#fireLinkCut()
* @see Liaison#fireFrameReceived(byte[])
*/
private void litDonnees() throws Exception
{
int taille = in.available();
byte buffer[] = new byte[taille];
in.read(buffer);
String traceLog = "Reception COM
"+BufferToString.toString(buffer,false);
if ( config.isTraceReceptionPortCom() || trace)
{
getLog().info(traceLog);
}
//Preparation de la journalisation des transactions foirees
logTransaction.info(traceLog);
if ( rechercheNoCarrier(buffer) )
{
fireLinkCut();
modeAT=true;
}
fireFrameReceived(buffer);
}
/**
* Recherche NO CARRIER ou RING dans une trame (rupture de
communication au niveau liaison.
* @param buffer les donnees dans lesquelles rechercher "NO CARRIER"
ou "RING"
* return true si NO CARRIER ou "RING" est detecte, false sinon
*/
private boolean rechercheNoCarrier(byte buffer[])
{
String str = new String(buffer);
if ( str.indexOf("NO CARRIER") != -1 )
return true;
else
return str.indexOf("RING") != -1;
}
/**
* Interprete de maniere asynchrone les donnees issues de commande
AT, a la recherche de RING et CONNECT.
* Si un RING est detecte, on extrait du ring les parametres de
l'appelant et de l'appele,
* si c'est le premier RING d'une communication, on decroche (envoi
de ATA), sinon on ne fait rien.
* Si c'est un CONNECT, on extrait la vitesse de connexion, on sort
du mode AT, on previent les
* listeners que la communication est engagee, et s'il reste des
donnees dans le buffer apres le connect,
* et que c'est une trame complete (cas d'une LR qui arrive dans la
foulee du connect), on previent les
* listeners qu'une nouvelle trame est dispo.
* @param commande la commande a interpreter
* @see Interpreteur
*/
private void interprete(String commande) throws Exception
{
if ( commande.indexOf("RING") != -1)
{
getLog().debug("LiaisonSerie.interprete : RING recu");
int indexCID = commande.indexOf("CID: ");
int indexDAD = commande.indexOf("DAD: ");
if ( ( indexCID != -1 ) && ( indexDAD != -1 ) )
{
cid = commande.substring(indexCID+5,indexDAD-1);
dad = commande.substring(indexDAD+5);
}
if ( ring == false )
{
getLog().debug("LiaisonSerie.interprete : RING recu, on decroche");
//un seul ATA a la fois
ring = true;
if (commandeAT("ATA","CONNECT",20000))
{
getLog().debug("LiaisonSerie.interprete : RING recu,
connection faite");
}
else
{
getLog().debug("LiaisonSerie.interprete : RING recu,
decrochage en timeout");
}
ring = false;

}
else
getLog().debug("LiaisonSerie.interprete : RING recu mais deja en
cours");
}
else if ( commande.indexOf("CONNECT") != -1)
{
getLog().debug("LiaisonSerie.interprete : CONNECT recu");
//String reception = (String)listeCommande.getLast();
int speed = 0;
try
{
int indexTx = commande.indexOf(":TX");
String strSpeed = commande.substring(indexTx-4,indexTx);
speed = Integer.parseInt(strSpeed);
}
catch(Exception e)
{
if ( getLog().isDebugEnabled())
{
getLog().debug("Format de CONNECT non connu "+commande);
}
//Recherche de "1200" et "2400" dans la chaine
if (( commande.indexOf("1200") != -1 ) && (
commande.indexOf("2400") == -1))
speed = 1200;
else
if (( commande.indexOf("2400") != -1 ) && (
commande.indexOf("1200") == -1))
speed = 2400;
}
modeAT = false;
fireLinkEstablished(cid,dad,speed);
//On envoie le reste aux objets a notifier
//Il arrive qu'une LR arrive dans la foulee du CONNECT
if ( resteTrame != null )
{
fireFrameReceived(resteTrame);
resteTrame=null;
}
}
else if ( commande.indexOf("NO CARRIER") != -1)
{
initialisationModem();
}
}
/**
* Methode d'ouverture du port.
* Ouvre le port, positionne les parametres du port serie, ajoute les
listeners
* et recupere les flots E/S du port.
*/
private void ouvrePort() throws Exception
{
getLog().debug("LiaisonSerie.ouvrePort");
CommPortIdentifier cpi =
CommPortIdentifier.getPortIdentifier(getPort());
sp = (SerialPort)cpi.open("Pad",1000);
int [] params = config.getPortParameters(getPort());
sp.setSerialPortParams(params[0],params[1],params[2],params[3]);
sp.addEventListener(this);
sp.notifyOnDataAvailable(true);
sp.notifyOnCarrierDetect(true);
in = sp.getInputStream();
out = sp.getOutputStream();
modeAT=true;
}
/**
* Methode d'initialisation du modem.
* Envoie un ensemble de commandes AT, presentes dans le fichier de
configuration
* pour initialiser le modem. Si le modem ne s'initialise pas au bout
de trois sequences de chaines AT,
* le port est ferme puis reouvert.
* @see Configuration#getChaineInitModem()
* @see #initialisationHard()
* @throws Exception si l'envoi des commandes AT se passe mal ou si
le modem ne repond pas OK suite a trois demandes d'initialisation
consecutives
*/
private synchronized void initialisationModem() throws Exception
{
//Pour essayer de resoudreles pb de com 10 bloque, on ferme
//et ouvre le port a chaque fin de communication
//3.0.3 : pas d'initialisation hard systematique
//initialisationHard();
boolean ok = false;
int numPassage = 0;
do
{
StringTokenizer st = new StringTokenizer(initModem,".");
while(st.hasMoreTokens())
{
ok = commandeAT(st.nextToken(),"OK",2000);
if ( !ok )
break;
}
numPassage++;
if ( numPassage == 3 )
{
numPassage = 0;
initialisationHard();
}
} while (!ok );
}
/**
* Reinitialise le port serie de maniere "brutale" en fermant le port
serie et en le rouvrant.
* Se retire comme destinataire des evenements du port serie, le
ferme et le rouvre.
*/
private void initialisationHard() throws Exception
{
sp.removeEventListener();
sp.close();
ouvrePort();
modeAT=true;
}
/**
* Lance une commande AT, et attend la reponse pendant un certain temps.
* @param commande la commande a envoyer
* @param reponse la reponse attendue
* @param timeOut le delai maximum d'attente de la reponse
* @return true si la reponse a ete recue, false si on est sorti en
timeout
* @throws Exception si l'ecriture sur le port serie a pose un pb
*/
private synchronized boolean commandeAT(String commande,String
reponse, int timeOut) throws Exception
{
if ( !this.arret )
{
String logInfo = "Emission COM "+commande;
if(config.isTraceEmissionPortCom())
{
getLog().info(logInfo);
}
logTransaction.info(logInfo);
out.write(commande.getBytes());
out.write((byte)0x0D);
out.write((byte)0x0A);
out.flush();
boolean fin = false, trouve = false;
//Boucle toutes les 100ms pour voir si les donnees attendues sont
presentes.
//Un wait est utilise, et non un sleep car la methode serialEvent
fait un notify pour prevenir
//les objets qui attendent que des donnees sont disponibles.
aChercher = reponse;
long debut = System.currentTimeMillis();
while(!fin)
{
if ( aChercher == null)
{
trouve = true;
fin = true;
}
else
{
if ( System.currentTimeMillis()-debut > timeOut)
{
fin = true;
trouve = false;
}
}
wait(100);
}
return trouve;
}
else
{
return true;
}
}
/**
* Cherche dans la liste des commabdes recues si la reponse attendue
est presente.
* Parcours la liste des commandes recues, et pour chacune, recherche
le parametre reponse
* @param reponse la reponse que l'on cherche
* @return true si la reponse a ete trouvee, false sinon
* @see #litAT()
*/
/* private boolean chercheReponse(String reponse)
{
Iterator iter = listeCommande.iterator();
while(iter.hasNext())
{
String commande = (String)iter.next();
if ( commande.indexOf(reponse) != -1)
return true;
}
return false;
}*/
/**
* Demande a la Liaison de tracer les lectures/ecritures sur le port
serie
* @param trace true pour demander l'activation des traces, false
pour leur inhibition
*/
public void setTrace(boolean trace)
{
getLog().debug("LiaionSerie.setTrace "+trace);
this.trace = trace;
}
/**
* True si modem est en mode AT (separateur = 0xD 0xA), false sinon
(separateur = 16 10 02 et 10 03)
*/
private boolean modeAT = true;

/**
* Port serie utilise
*/
private SerialPort sp;
/**
* Flot d'entree du port serie
*/
private InputStream in;
/**
* Flot de sortie du port serie
*/
private OutputStream out;
/**
* Chaine d'initialisation du modem
*/
private final String initModem =
Configuration.getInstance().getChaineInitModem();
/**
* Liste des commandes AT recues en une trame
*/
//private LinkedList listeCommande;
/**
* Reste d'une trame non complete sous forme String
*/
private String reliquat;
/**
* Reste d'une trame non complete sous forme byte []
*/
private byte [] resteTrame;
/**
* Logger utiliser pour la journalisation
*/
private Logger logTransaction;
/**
* Positionne a true si on a deja rempondu a un ring, false sinon
*/
private boolean ring = false;
/**
* Chaine contenant le numero de telephone de l'appelant et celui de
l'appele
*/
private String cid,dad;
/**
* Contient la configuration de l'application
*/
private Configuration config;
/**
* Tableau permettant de construire la chaine contenant les
delimiteurs separant les commandes AT.
*/
private final static char [] tabDa = { (char)0xD,(char)0xA };
/**
* Chaine contenant les delimiteurs permettant de separer des
commandes AT
*/
private final static String da = new String(tabDa);
/**
* Chaine que l'on attend en reponse a une commande AT
*/
private String aChercher = null;
/**
* True si l'on doit tracer les E/S, faux sinon.
*/
private boolean trace = false;
/**
* True si le port est en cours d'arret.
*/
private boolean arret = false;
/**
* Classe permettant d'interpreter une commande AT (RING ou CONNECT)
de maniere
* asynchrone
*/
private class Interpreteur extends Thread
{
/**
* Contient la commande a interpreter, demarre le thread
*/
public Interpreteur(String commande)
{
super("Interpreteur "+commande);
this.commande = commande;
start();
}
/**
* Appelle interprete
* @see #interprete(String)
*/
public void run()
{
try
{
interprete(commande);
}
catch(Exception e)
{
JavaPad.getInstance().ajouteErreur(e.getMessage());
getLog().error("Erreur interpretation "+commande,e);
}
}
/**
* La commande a interpreter
*/
private String commande;
}
}