Laden...

CAPI-Wrapper

Erstellt von tom-essen vor 17 Jahren Letzter Beitrag vor 14 Jahren 67.471 Views
tom-essen Themenstarter:in
1.820 Beiträge seit 2005
vor 17 Jahren
CAPI-Wrapper

Hallo!

Möchte mal u.a. aus persönlichem Interesse versuchen, hier einen zentralen Thread für CAPI zu schaffen.
Jedem, der jetzt den Einwand erhebt, dass es sowas ja schon gibt (AKA ISDN LIB), sei gesagt, dass diese - zumindest nach meinem Kenntnisstand - nicht mehr kostenlos erhältlich ist.

Das Programm ist rel. einfach gehalten:
Es registriert sich beim Start automatisch an der CAPI-Schnittstelle und zeigt in der ListView anschließend alle CAPI-Messages an, die über CAPI reinkommen. Über eine testcapi.config-Datei im Programm-Verzeichnis kann man zusätzlich einstellen, welche Anrufe wann gefiltert werden sollen. Hier mal eine Beispiel-Datei

<?xml version="1.0"?>
<testcapi>
  <logfile>c:\isdn-log.log</logfile>
  <blacklist>unbekannt</blacklist>
  <filter>
    <callingpartynr>12345.*</callingpartynr>
    <mintime>09:00:00</mintime>
  </filter>
</testcapi>

Damit werden zum einen alle eingehenden Anrufe in der Datei 'c:\isdn-log.log' protokolliert. Weiterhin kann man verschiedene Filter einrichten, wobei filtern in diesem Fall 'hartes Auflegen' bedeutet (abheben und auflegen).
Zum einen gibt es die Blacklist: Alle Anrufer in der Blacklist, in diesem Fall mit Rufnummernunterdrückung (unbekannt) werden immer gefiltert. Wenn man allerdings innerhalb von 45 Sekunden (hardcoded im Programm) nochmals anruft, kommt man durch. Da nahezu alle Umfragen, .... ihre Nummer nicht rausgeben, kann man sich mit dieser Regel einiges an Telefon-Spam entledigen. Geht allerdings nur, wenn die meisten aus dem Freundes- und Verwandten-Kreis ihre Nummer übermitteln, da durch das harte Auflegen u.a. eine Einheit berechnet wird (OK, bei Flatrates uninteressant).

In den Filtern kann man etwas genauer werden: Die callingpartnr nennt die zu filternde Nummer, wobei hier reguläre Ausdrücke erlaubt sind, mit mintime und maxtime kann man Zeitschranken festlegen, wann der Anruf gefiltert werden soll.
Im o.g. Fall werden alle Orts-Nummern, welche mit 12345 beginnen, vor 9 Uhr morgens gefiltert.

Der CAPI-Wrapper ist als LowLevel-Komponente gedacht, welche zunächst nur die CAPI-Schnittstelle kapselt. Höhere Funktionalitäten, wie z.B. Gesprächsverwaltung, Fax-Funktionalität, Anrufbeantworter, ... soll in einer weiteren Assembly gekapselt werden.

Ich hoffe, damit einigen helfen zu können.

Download:
Da der "Testcapi"-Download mittlerweile nicht mehr funktioniert, und die Installationsdatei zu groß für das Forum ist, hier der Link zur Capi-Wrapper-Installationsdatei. Das Beispiel aus dem "Testcapi"-Download sollte aber auch auf den neuen Wrapper anwendbar sein.

Literatur:1.Grundlagen der Netzwerkprogrammierung: http://www.shamrock.de/dfu/index.htm?dfu3.htm#capi 1.ISDN-Programmierung 1.Offical CAPI document downloads, wahrscheinlich die wichtigste Seite 1.ISDN-Seite

Verwandte Foren-Threads:*Auf CAPI zugreifen *Capi oder nichtCapi das ist hier die Frage *DLL-Funktionen nutzen *Telefonanruf über Modem/ISDN-Karte und Abspielen eines Sounds. *c# unter Linux und Windows. Alles was mit Telefonie zu tun hat *VoIP SDK

Nobody is perfect. I'm sad, i'm not nobody 🙁

K
21 Beiträge seit 2005
vor 17 Jahren
Capi Wrapper

Hallo,

das Project werde ich mit den grössten Interesse verfolgen.

Du hast recht die bisherigen Capi Librarys kannste vergessen.

Ich müsste aus beruflicher Sicht ebensfalls Capi Funktionen entwickeln.

Bin dann bei Eicon SDK gelandet, leider ist die Dokumentation mehr schlecht als recht.

Über die 1.200 Euro für die 4xSo Karte Bri4 will ich gar nicht sprechen.

Wäre schon wenn du den Wrapper zu download anbittest.

Ich wünsche Dir viel erfolg....

mfg

H
240 Beiträge seit 2006
vor 17 Jahren

Hallo,

sehr schöne Idee das ganze und sieht auch sehr gut aus so weit!

Ich habe das alles schonmal vor ca. 1 Jahr in meinem Vorpraktikum fürs Studium gemacht (in C++).
Jetzt bin ich für mein Praxissemester in der selben Firma und soll das ganze um einen WebService erweitern. Das Problem dabei ist nur, dass die alten Sourcen, an denen ich 8 Wochen geschraubt habe, nicht mehr auffindbar sind und die DLL nur 2 public Methoden hat, die allerdings auf den Versand von SMS spezialisiert sind.
deshalb muss ich möglicherweise alles nochmal in C# machen, was euch natürlich zu Gute käme...

ich sach mal, ich meld mich dann nochmal...

Gruß
hulkstar

MfG hulkstar

tom-essen Themenstarter:in
1.820 Beiträge seit 2005
vor 17 Jahren

Hallo!

Letztes Update des Quellcodes war am 23.10.2006 um ca. 19:50:22.523

Hab' jetzt hoffentlich die grundlegende Nachrichtenstruktur korrekt implementiert und schonmal die Klassenrümpfe für die einzelnen CAPI-Nachrichten entworfen.
Jetzt muss ich noch irgendwie ein Array basteln, welches den jeweiligen CAPI-Commands die entsprechende Nachrichten-Klasse zuordnet. Evtl. hat da jemand eine Idee ?!?!

Dateidownload werde ich noch nicht anbieten, weil ...
... noch alles in einer Datei ist
... ich komplett auf VS2005 umgestellt habe, was wahrscheinlich noch nicht jeder hat.

@hulkstar:
Die Sache mit dem WebService ist eine interessante Erweiterungsidee ... 😉

Nobody is perfect. I'm sad, i'm not nobody 🙁

H
240 Beiträge seit 2006
vor 17 Jahren

hallo,

ich bin einen anderen Weg gegangen. Habe alle Nachrichten als Strukturen erstellt, weil ich es so in C++ auch hatte. Allerdings bin ich dann über das Problem gestolpert, dass es in C# keine Unions mehr gibt und mit StructLayout(LayoutKind.Explicit) und FieldOffset() hat es nicht funktioniert weil man da wohl keine Strukturen überlagern kann, sondern nur "normale" variablen.
Also habe ich es jetzt so:


[StructLayout(LayoutKind.Sequential)]
public struct message_Header
{
   public ushort length;
   public ushort applID;
   public byte command;
   public byte subcommand;
   public ushort message_number;
}

[StructLayout(LayoutKind.Sequential)]
public struct connect_req
{
   public message_Header header;
   public int controller;
   public ushort CIP_value;
   public byte[] structs;
}

In das byte[] structs kommen dann die Daten der fehlenden Strukturen. Das Problem ist, dass das alles "per Hand" byteweise eingegeben werden muss:


 // Connect_req an capi senden.(zumindest versuchen)
            connect_req cr = new connect_req();            
            cr.header.applID = (ushort)c.appId;
            cr.header.command = 0x02;
            cr.header.subcommand = 0x80;            
            cr.header.message_number = 0;
            
            cr.controller = 1; // controller 1
            cr.CIP_value = 2; // unrestricted digital information
            cr.structs = new byte[43];
            // called party number struct
            cr.structs[bcount++]   = (byte)12; // länge des called party structs
            cr.structs[bcount++] = 0x80;        // numbering plan identification (default)
            cr.structs[bcount++] = (byte)'0';   // gerufene nummer 0171XXXXXXX
            cr.structs[bcount++] = (byte)'1';   
            cr.structs[bcount++] = (byte)'7';   
            cr.structs[bcount++] = (byte)'1';
            cr.structs[bcount++] = (byte)'X';
            cr.structs[bcount++] = (byte)'X';
            cr.structs[bcount++] = (byte)'X';
            cr.structs[bcount++] = (byte)'X';
            cr.structs[bcount++] = (byte)'X';
            cr.structs[bcount++] = (byte)'X';
            cr.structs[bcount++] = (byte)'X';
            //calling party number struct
            cr.structs[bcount++] = 0;   // Keine daten           
            //called party subaddress
            cr.structs[bcount++] = 0;    // nix
            //calling party subaddress
            cr.structs[bcount++] = (byte)5;     // Länge des structs
            cr.structs[bcount++] = 0x80;        // (default)
            cr.structs[bcount++] = 0x49;        // H
            cr.structs[bcount++] = 0x75;        // u
            cr.structs[bcount++] = 0x6C;        // l
            cr.structs[bcount++] = 0x6B;        // k    
            // b protocoll
            cr.structs[bcount++] = (byte)9;     // länge des structs
            cr.structs[bcount++] = (byte)1;     // B1 Protocoll
            cr.structs[bcount++] = 0;           // B2 Protocoll
            cr.structs[bcount++] = 0;           // B3 Protocoll
            cr.structs[bcount++] = 0;           // B1 Conf
            cr.structs[bcount++] = 0;           // B2 Conf
            cr.structs[bcount++] = 0;           // B3 Conf
            cr.structs[bcount++] = 0;           // Global Conf
            cr.structs[bcount++] = 0;
            cr.structs[bcount++] = 0;
            //BC
            cr.structs[bcount++] = 0;
            //LLC
            cr.structs[bcount++] = 0;
            //HLC
            cr.structs[bcount++] = 0;
            // Additional info
            cr.structs[bcount++] = 0;

            cr.header.length = (ushort)(bcount + 1 + Marshal.SizeOf(cr.header)+Marshal.SizeOf(cr.CIP_value)+Marshal.SizeOf(cr.controller));
            int erg = Capi.CAPI_PUT_MESSAGE(c.appID, out cr);

(Das ganze funktioniert noch nicht. ich kann die Struktur zwar so an die Capi übergeben, bekomme aber fehlercode 0x1102, weil ich noch an den Parametern am schrauben bin (guck mir dazu versch. Traces an und vergleiche es mit meiner Struktur).) Nagut, umständlich ist es auch, man könnte einige Daten mit Schleifen eingeben aber das ganze ist ja bosher nur ein Test...

Hier einige meiner Probleme: um einen ordentlichen Vergleich mit den Traces zu machen, müsste ich meine komplette Struktur als Hex ausgeben, weiss aber nicht, wie das geht. Vorschläge? Oder kennt jemand ne gute Tracecapi, die die gesamten mesages ausgibt, nicht nur einen teil wie die von AVM?

Zum anderen musste ich für den Test die Parameterliste von CAPI_PUT_MESSAGE auf "(int appID, out connect_req)" ändern, was natürlich wieder verhindert, dass ich andere Messagetypen senden kann...

Hast du deinen Weg schon getestet, also sprich, hast du schonmal ein Connect_Req o.ä. an die Capi übergeben und eine Antwort bekommen?
Willst du die ConvertToByteArray für jede Capimessage überschreiben, damit die individuellen Parameter auch umgewandelt werden?
Fragen über Fragen...

Wenn dieser Message-Kram fertig ist will ich ne Statemachine zum Verwalten der Verbindung bauen, aber dazu brauche ich funktionierende Messages. X(
Ich hoffe, irgendjemand liesst das bald und hat ein paar tips für mich, ich komm nämlich im Moment nicht so richtig weiter...

hulkstar

MfG hulkstar

E
26 Beiträge seit 2004
vor 17 Jahren

hallo,

wenn Du schon C oder C++ Code hast, solltest Du ihn benutzen um ein
Assembly daraus zu machen. Mit entsprechenden C++ CLI Wrappern
kannst Du Deinen alten CAPI C/C++ Code wunderbar weiter benutzen
und eine managed Assembly daraus machen.

Es ist relativ blödsinning und unproduktiv mit C# alles von Grund auf
neu zu erfinden mit neuen Fehlern. Die Zeit spare ich mir, mein CAPI C++ Code läuft
seit über 10 Jahren in jeder Windows Umgebung (ob User oder Kernel Mode)
und jetzt auch in einem Managed Wrapper.

elli

H
240 Beiträge seit 2006
vor 17 Jahren

Das Problem ist, dass mein C-Code nicht die Methoden bereitstellt, die ich jetzt brauche. Ich müsste also nochmal in C eintauchen und da hab ich eigentlich keine Lust zu, da der Code sehr unübersichtlich ist und ich alles auseinanderfriemeln müsste...
Außerdem ist der Code nicht mehr komplett vorhanden, weil mein Betreuer den wohl auf einem seiner zahlreichen Rechner verschlampt hat (Nur die DLL ist im einsatz)...

Sobald das mit den Messages gelöst ist, ist der Rest ja auch "kein so großes Problem mehr" nur da haperts halt im Moment. Deshalb interessiert mich auch brennend, ob die Lösung von Tom funktioniert...

MfG hulkstar

E
26 Beiträge seit 2004
vor 17 Jahren

Hallo,

OK verstehe. Bei meinen ersten C# Übungen habe ich einen anderen Ansatzt
gewählt. Das dumme Struktur Nachbilden ist sowiso für die Katz da alles
variante Strukturen sind. Ich habe also eine Message definiert die einen
unmanged buffer enthält und die einzelnen Element per Marshall etc.
setzt. Hier mal mein alter Code als Idee. Ich programmiere CAPI und ISDN
Applikationen und Treiber seit nun fast 20 Jahren und langsam wird es langweilig 🙂

elli


using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;


namespace CAPI
{

 public class CapiMessage : IDisposable
  {
#region Constructor, Desctructor, Dispose
   public CapiMessage(CapiApplication application)
   {
    this.application = application;
    internalMemory   = Marshal.AllocHGlobal( 2048 + 32 );
    umBuffer         = internalMemory;
    writeIndex       = 8;
    length           = 0;
    streamBuffer     = new byte[2048 + 32 ];

    if ( streamBuffer == null )
     {
      //throw()
     }


   }

   public CapiMessage( IntPtr msg)
   {
    // Save unmanged message buffer position
    streamBuffer   = new byte[2048 + 32 ];

    Marshal.Copy( msg, streamBuffer,0,8);


    length  = readWord(0);
    command = (CapiCommand) (readWord(4) & 0xFFFF);

    // check for DATA B3 Indication

    Marshal.Copy( msg, streamBuffer,0,length);
   }

   ~CapiMessage()
   {
    if( internalMemory != IntPtr.Zero )
    {
     Marshal.FreeHGlobal( internalMemory );
    }
   }

   public void Dispose()
   {
    if ( ! fDisposed )
     {
      fDisposed = true;
      umBuffer = IntPtr.Zero;

      //umBuffer =  IntPtr.Zero;
      System.GC.SuppressFinalize( this );
     }

   }
#endregion


   public int sendMessage  (CapiCommand command)
    {
     this.command = command;
     writeIndex   = 0;
     length      += 8;

     writeWord( (ushort) length );
     writeWord( (ushort) application.ApplicationID );
     writeWord( (ushort) command );
     writeWord( (ushort) application.NextMessageNumber);

     return application.putMessage(this, command,length );
    }

#region message request section

   public int listenRequest(
                             int     controllerNumber,
                             Int32   infoMask,
                             CIPMask cipMask,
                             Int32   cipMask2
                           )
    {
     reset();
     writeDWord( controllerNumber);
     writeDWord( infoMask );
     writeDWord( (Int32) cipMask );
     writeDWord( cipMask2 );
     writeStruct( null );
     writeStruct( null );

     return sendMessage( CapiCommand.cmListenRequest );
    }

  public int connectRequest(
                            int       controllerNumber,
                            string    calledPartyNumber,
                            string    callingPartyNumber,
                            CIPValue  cipValue,
                            BProtocol bProtocol
                           )
  {
   reset();
   writeDWord( controllerNumber );
   writeWord( (ushort) cipValue );

   writeCalledPartyNumber(calledPartyNumber);
   writeCallingPartyNumber(callingPartyNumber,cipValue);

   writeStruct( null );               // Called  party subaddress
   writeStruct( null );               // Calling party subaddress
   writeStruct( null );               // param B protocol
   writeStruct( null );               // Bearer Capaility
   writeStruct( null );               // Low Layer Compatibility
   writeStruct( null );               // High Layer Compatibility
   writeStruct( null );               // Additional information elements

   return sendMessage(CAPI.CapiCommand.cmConnectRequest);
  }

  public int disconnectRequest(Int32 plci)
  {
   reset();
   writeDWord( plci );
   return sendMessage(CAPI.CapiCommand.cmDisconnectRequest);
  }

  public int alertRequest(Int32 plci)
  {
   reset();
   writeDWord( plci );
   writeStruct( null );               // Additional information elements
   return sendMessage(CAPI.CapiCommand.cmAlertRequest);
  }

  public int connectB3Request(Int32 plci)
  {
   reset();
   writeDWord( plci );
   writeStruct( null );               // NCPI
   return sendMessage(CAPI.CapiCommand.cmConnectB3Request);
  }

  public int disconnectB3Request(Int32 plci)
  {
   reset();
   writeDWord( plci );
   writeStruct( null );               // NCPI
   return sendMessage(CAPI.CapiCommand.cmDisconnectB3Request);
  }

#endregion

#region message response section


  public int connectResponse(Int32 plci, int reason)
   {
    reset();
    writeDWord( plci );
    writeWord( (ushort) reason );
    writeStruct( null );            // B protocol struct
    writeStruct( null );            // Connected number
    writeStruct( null );            // Connected subaddress
    writeStruct( null );            // Low Layer Compatibility
    writeStruct( null );            // Additional information elements

    return sendMessage(CAPI.CapiCommand.cmConnectResponse);
   }

  public int connectActiveResponse(Int32 plci)
  {
   reset();
   writeDWord( plci );

   return sendMessage(CAPI.CapiCommand.cmConnectActiveResponse);
  }

  public int disconnectResponse(Int32 plci)
  {
   reset();
   writeDWord( plci );

   return sendMessage(CAPI.CapiCommand.cmDisconnectResponse);
  }

  public int infoResponse(Int32 plci)
  {
   reset();
   writeDWord( plci );

   return sendMessage(CAPI.CapiCommand.cmDisconnectResponse);
  }

  public int disconnectB3Response(Int32 ncci)
  {
   reset();
   writeDWord( ncci );
   writeStruct( null );               // NCPI

   return sendMessage(CAPI.CapiCommand.cmDisconnectB3Response);
  }

  public int connectB3Response(Int32 ncci,int reject)
  {
   reset();
   writeDWord( ncci );
   writeWord( (ushort) reject );
   writeStruct( null );               // NCPI

   return sendMessage(CAPI.CapiCommand.cmConnectResponse);
  }

  public int resetB3Response(Int32 ncci)
  {
   reset();
   writeDWord( ncci );

   return sendMessage(CAPI.CapiCommand.cmResetB3Response);
  }

  public int data3Response(Int32 ncci)
  {
   reset();
   writeDWord( ncci );

   return sendMessage(CAPI.CapiCommand.cmDataB3Response);
  }

  public int connectB3ActiveResponse(Int32 ncci)
  {
   reset();
   writeDWord( ncci );

   return sendMessage(CAPI.CapiCommand.cmConnectB3ActiveResponse);
  }


#endregion

  protected void reset()
   {
    writeIndex = 8;
    length     = 0;
   }

  public byte[] MessageBuffer
  {
   get
   {
    return streamBuffer;
   }
  }

#region Message properties (member access section)
  public int ApplicationID
  {
   get
   {
    return readWord(0);
   }
  }

  public CapiCommand Command
  {
   get { return command;  }
   set { command = value; }
  }

  public ushort  MessageNumber
  {
   get
   {
    return readWord(6);
   }
  }

   public int  Length
   {
    get
    {
     return length;
    }
   }

    public Int32  Plci
     {
      get
      {
       switch ( command )
        {
         case CapiCommand.cmConnectRequest:
         case CapiCommand.cmListenRequest:
           return 0;  // Throw ??

        }
       return readDWord(8);
      }
     }

  public Int32  Ncci
  {
   get
   {
    return readDWord(8);
   }
  }

  public int ControllerNumber
   {
    get // todo check request type
     {
      return readDWord(8);
     }
   }

  public int InfoMask
   {
    get
     {
      return readDWord(12);
     }
   }

  public int CIPMask
  {
   get
   {
    return readDWord(16);
   }
  }

  public CapiInfo Info
  {
   get
   {
    switch ( command )
     {
      case CapiCommand.cmDataB3Confirmation:
        return (CapiInfo) readWord(14);
     }
    return (CapiInfo) readWord(12);
   }
  }

  public string CalledPartyNumber
   {
    get { return getCalledPartyNumber();  }
   }
   
  public string CalledPartySubAddress
   {
    get { return getCalledPartySubAddress();  }
   }

  public string CallingPartyNumber
   {
    get { return getCallingPartyNumber();  }
   }
   
  public string CallingPartySubAddress
   {
    get { return getCallingPartySubAddress();  }
   }
   
  public CIPValue CIP 
   {
    get { return (CIPValue) readWord(4);   }
    
   } 
   
   
  
#endregion

# region  Low level Message assembly stuff

  /// <summary>
  /// Write an byte into bufferStream, update the writeIndex and the written length
  /// </summary>
  protected void writeByte( byte val )
   {
    //Marshal.WriteByte( umBuffer, writeIndex, val );
    streamBuffer[writeIndex++] = val;
    length++;
   }

  /// <summary>
  /// Write an short into bufferStream in little endian byte order.
  /// </summary>
  protected void writeWord( ushort val )
   {
    writeByte( (byte) val );
    writeByte( (byte) (val >> 8) );
   }

  /// <summary>
  /// Write an int into bufferStream in little endian byte order.
  /// </summary>
  protected void writeDWord( int val )
   {
    writeWord( (ushort) val );
    writeWord( (ushort) (val >> 16));
   }

  protected void writeStruct( object val )
   {
    if( val == null )
    {
     writeByte(0);
     return;
    }

    int size = Marshal.SizeOf( val );
    if( size < 255 )
    {
     Marshal.WriteByte( umBuffer, writeIndex, (byte) size );
     writeIndex++;
     length++;
    }
    else
    {
     Marshal.WriteByte( (IntPtr) writeIndex, 0, 255 );
     writeIndex++;
     length++;
     Marshal.WriteInt16( (IntPtr) writeIndex, 0, (short) size );
     writeIndex += 2;
     length     += 2;
    }
    Marshal.StructureToPtr( val, (IntPtr) writeIndex, false );
    writeIndex += size;
    length     += size;
   }

  protected void writeCalledPartyNumber(string number)
   {
    writeByte( (byte) (1 + number.Length));    // length
    writeByte( 0x80 );                        // type

    foreach( char ch in number)
     {
      writeByte( (byte) ch );
     }

   }

  protected void writeCallingPartyNumber(string number, CIPValue cipValue)
  {
   writeByte( (byte) (2 + number.Length));    // length
   writeByte( 0x00 );                         // type

   if ( cipValue == CIPValue.cipValueNoProfile)
    {
     writeByte( 0x81 );                         // screen indicatior
    }
   else
    {
     writeByte( 0x80 );                         // screen indicatior
    }

   foreach( char ch in number)
   {
    writeByte( (byte) ch );
   }
  }

   protected void writeNCPI( NCPI ncpi )
    {
     if( ncpi  == null )
     {
      writeByte(0);
      return;
     }
    }





#endregion

#region Low Level Message disassembly section
  private string getCallingPartyNumber()
  {
   return "";
  }
   
  private string getCallingPartySubAddress()
  {
   return "";
  }
  
  private string getCalledPartySubAddress()
  {
   return "";
  }
   
   
  private string getCalledPartyNumber()
  {
   // BYTE      length
   // BYTE      type;
   // BYTE      digits[ capiMaxAddressLength ];

   int    offset;
   int    length;

   switch ( command )
   {
    case CapiCommand.cmConnectRequest:
    case CapiCommand.cmConnectIndication:
    case CapiCommand.cmInfoRequest:
     offset = seekStructure(0,1);
     length = readByte(offset++);
     return getString( length - 1, offset );


    default:
     return "";
   }
    
  }

  private int seekStructure(int offset, int index)
  {
   int length;

   offset += 8;  // byte offset into variable message part

   while ( --index > 0)
   {
    length = readByte( offset );

    if ( length + offset > Length )
    {
     Trace.WriteLine("! bad structure at index "+index);
     return 0;
    }

    offset += (length + 1);
   }


   return offset;
  }
   
  private string getString(int length, int offset)
  {
   string st;
    
   st = "";
    
   return st;
  }



  public byte readByte(int index )
  {
   return streamBuffer[index];
  }

  public ushort readWord(int index)
  {
   ushort lb;
   ushort hb;

   lb   = streamBuffer[index];
   hb   = streamBuffer[index+1];
   hb <<= 8;
   hb |= lb;
   return hb;
  }

  public int readDWord(int index  )
  {
   int lb;
   int hb;

   lb = readWord(index);
   hb = readWord(index+2);

   return (hb << 16)|lb;
  }
#endregion

#region Private identifier
   private IntPtr      umBuffer;
   private IntPtr      internalMemory;
   private int         writeIndex;
   private CapiCommand command;
   private int         length;
   bool                fDisposed;
   byte []             streamBuffer;
   CapiApplication     application;

#endregion
  }



}

H
240 Beiträge seit 2006
vor 17 Jahren

Hallo elli,

vielen Dank für deinen Code!
Er hat mir ein bisschen geholfen aber so ganz klar komm ich damit noch nicht.

Ich habe mir eine Main-Methode geschrieben, mit der ich einfach mal eine ganz einfache Message testen wollte...

Habe mir dazu die Listen-Request rausgesucht und aufgerufen mit:


CapiWrapper.Capi c = new Capi(); // Das ist die Klasse von tom-essen aber ausgeschlachtet bis auf die DLLImport-Aufrufe, den Konstruktor und die enums...
CapiMessage cm = new CapiMessage(c); // das is deine ;)
int erg = new cm.listenRequest(1, 0, CIPMask.IgnoreIncomingCalls, 0);

CIPMask hab ich (erstmal) folgenderweise gebaut:


public enum CIPMask : int
{
   IgnoreIncomingCalls = 0,
   UnrestrictedDigitalInformation = 2
}

und da CapiMessage.sendmessage() die Methode putMessage(this, command, length) der Capi-Klasse aufruft, hab ich die da eingefügt (so wie ich sie verstanden hab):


internal int putMessage(capiMessage capimsg, CapiMessages command, int length)
{
   return CAPI_PUT_MESSAGE(this.appID, out capimsg);
   // die Signatur von CAPI_PUT_MESSAGE musste ich dafür auf (int appID, out CapiMessage message) ändern...
}

Allerdings weiß ich nicht, warum du die Länge da nochmal mitübergibst, da CAPI_PUT_MESSAGE nur die ersten 2 Argumente nimmt...
Irgendwas muss ich auf jeden Fall extremst falsch verstanden oder übersehen haben, da ich wie vorher schon bei meinem Code die Antwort 0x1102 = "Illegal Command or Subcommand or message length less than 12 octets" bekomme.
Kannst du mir da vielleicht nochmal auf die Sprünge helfen? Ich sitz hier jetzt schon seit tagen und komme eigentlich überhaupt nicht vorwärts...

EDIT: ok, da war ich wohl noch ein bisschen müde heute morgen. du übergibtst wahrscheinlich nur den messagBuffer an CAPI_PUT_MESSAGE, oder lieg ich da jez auch wieder völlig falsch? hab das jetzt auch schon probiert (den DLLImport entsprechend auf IntPtr geändert) aber ich krieg die daten trotzdem nich zur CAPI rübergeschoben, sie antwortet mir immernoch mit 0x1102...
ENDEDIT

Vielen Dank schonmal!
hulkstar

MfG hulkstar

I
1.739 Beiträge seit 2005
vor 17 Jahren
Kleine Anmerkung/Vorschlag

Ich würde vorschlagen, gerade bei Artikeln den Code, im Hintergrund zu lassen bzw. nur wesentliche Teile zu posten. Der komplette Code lässt sich auch prima als Download anhängen. Das erhöht die Lesbarkeit des Artikels und dem angehängten/selben Thread.

[EDIT von herbivore]verschoben nach .NET-Komponentensammlung[/EDIT]

E
26 Beiträge seit 2004
vor 17 Jahren

Hallo,

habe vergessen mal wieder vorbeizuschauen, bzw. bin total dicht und arbeite 14h am Tag ...
Anyway, ich schicke die mal meinen ganzen Code. Ist extrem unvollständig, eine Linie
müßte aber zu erkennen sein (ein Ansatz wie ma programmieren könnte) und wenn ich mich nicht irre konnten ankommende Rufe gemonitort werden. Da ich inzwischen wie gesagt alles in C++ CLI habe brauche ich die C# Linie nicht weiter zu verfolgen. Ein C++ Kernel hat den schönen Vorteil das ich ihn überall verwenden kann.

Mal sehen ob ich den Anhang schaffe...

elli

H
240 Beiträge seit 2006
vor 17 Jahren

Vielen Dank, Elli!

Ich muss gestehen, ich hab in den Code noch nicht wieder reingeguckt, wollt es selber versuchen.
Das hat auch so weit geklappt. Allerdings hab ich jetzt folgendes Problem:
Ich kann nen Connect_Req senden und bekomme auch einen Connect_Conf mit Info==0. Also sollte alles in Ordnung sein.
Allerdings sendet die Capi mir dann kein Connect_Active_Ind, das ja dann eigentlich kommen müsste (laut Dokumentation von Capi.org, Teil 1, Seite 133).
Beim Pollen auf die Messagequeue bekomm ich immer nur 0x1104 = Queue leer.
Wenn ich als Zielnummer meine Handynummer angebe blinkt ganz kurz "Anruf 1" im Display aber ohne, dass ein Klingel ausgelöst wird oder ich den Anruf annehmen kann.
Werde gleich mal probieren, trotzdem einen Connect_B3_Req zu senden aber ich glaube nicht, dass das funktioniert...

Da du meintest, dass du schon seit 20 Jahren CAPI programmierst, hast du ja vllt eine Idee, wo mein Fehler liegen könnte?
Mit Phoner kann ich mein Handy ohne Probleme anrufen, also liegts auch nicht an der hardware... 🙁

Gruß!
hulkstar

P.S. ich weiss nicht, ob das überhaupt noch in diesen Thread soll oder lieber in einen eigenen. Bei Bedarf bitte verschieben, danke!

Nachtrag:
Habe getestet, einen Connect_B3_Req zu senden. bekomme auch einen Connect_B3_Conf als Antwort, allerdings mit Info = 0x 2001 = Message not supportet in current state.
An den B-Protokollen rumzuspielen bringt auch nix (hatte ich irgendwo gelesen), dann krieg ich immer die Fehlermeldung Bx-Protokoll nich unterstützt...

Nachtrag2:
Habe "zum Spass" mal die Nummer des SMS-Servers angegeben, den ich dann später mit dem Programm erreichen will. Bei ihm klappt der Rufaufbau und ich bekomme mein Connect_Act_Ind von der CAPI. Kann mir im Moment nicht vorstellen, woran es liegen könnte, dass das mit meinem Handy nicht funktioniert und meine auch, dass ich die Probleme bei meiner C++-Implementation nicht hatte...

Nachtrag3: Handys und Telefone kann man anrufen, wenn man beim Connect Request CIP Value auf 1 (=Speech) stellt. (ich depp!)

MfG hulkstar

tom-essen Themenstarter:in
1.820 Beiträge seit 2005
vor 17 Jahren
Thread-Zugehörigkeit

Hallo!

@Hulkstar:
Das mit dem Thread geht von meiner Seite aus in Ordnung, im Großen und Ganzen geht es ja immer noch um die Kapselung der CAPI-Funktionalität in C#.

Und bisher bin ich mit dem Verlauf hier mehr als Zufrieden, kann mich nur grad' nicht so sehr darum kümmern, da ich z.Zt. am TWAIN-Wrapper arbeite.

Nobody is perfect. I'm sad, i'm not nobody 🙁

E
26 Beiträge seit 2004
vor 17 Jahren

hallo,

vergesse immer reinzuschauen 🙂

Selbst wenn die CIP etc. nicht stimmt, muß der Ruf mit einem Disconnect Indication terminieren und anzeigen das der PLCI vom Connect Confirmation nicht mehr gültig ist.

Gerade bei den Sprachen und Voice CIP kann es lustig werden...

elli

194 Beiträge seit 2006
vor 17 Jahren

Hallo,
Ich habe so einfach beim lesen dies mitbekommen.

Original von elli
wenn Du schon C oder C++ Code hast, solltest Du ihn benutzen um ein
Assembly daraus zu machen. Mit entsprechenden C++ CLI Wrappern
kannst Du Deinen alten CAPI C/C++ Code wunderbar weiter benutzen
und eine managed Assembly daraus machen.

Es gibt doch das CAPI-ADK von AVM (Supermächtig das Teil, da von AVM), welches frei zur Verfügung steht.
Könnte man das nicht per C++/CLI wrappen ?
(Ich habe allerdings keine Ahnung von C++)
Würde mich allerdings sehr interessieren, diese Art von Coderecycling.

Gruss

Balaban_S

E
26 Beiträge seit 2004
vor 17 Jahren

@Balaban_S

Es gibt doch das CAPI-ADK von AVM (Supermächtig das Teil, da von AVM), welches frei zur Verfügung steht.
Könnte man das nicht per C++/CLI wrappen ?

Sollte kein Problem sein. Allerdings ist es ja C-Code und Du musst daraus erst
mal eine Art Obekt Modell machen. Aber nicht in der Art ich mache ein .NET Low Level CAPI Schnittstelle. Davon halte ich überhaupt nichts. Wir haben es hier immer mit
Verbindungen zu tun und man sollte da eine andere Sicht haben. So in der Art

IsdnConnection
Connect
Disconnect
AcceptCall
RejectCall
IgnoreCall
Transmit
DataResponse

und Ereignisse der Art
OnConnectionEstablished
OnConnectionFailed
OnIncomingCall
OnDataReceived
OnDataTransmitted

...

mit dieser Art des herangehens (im Grund mehr oder weniger OSI-ISO - Schichtenmodell) kann man jede Art von Kommunilationsmodell abbilden.

Sei es ISDN, Bluetooth, TCPIP ...

elli

194 Beiträge seit 2006
vor 17 Jahren

Ja, du hast Recht. Ich find deine Komponente auch sehr gut jedoch habe ich es bis jetzt nicht geschafft einen Ton aus dem Lautsprecher zu kriegen.
Wie sollte das vor sich gehen ?

Versteh mich nicht falsch.

Alos ich mache eine Connect_Req auf mein Handy
(ISDN.Connect()via deiner Komponente)
Und es klingelt!
Zum probieren lass ich 3 Mal läuten und dann sollte mein Anrufbeantworter
einspringen.
Währenddessen habe ich verschiedene
**Connect_B3_Ind 's **unter anderem auch
Connect_B3_Active_Ind
In der ankommenden CapiMessage ist der ByteStream enthalten.
Wie komme ich an die Audioausgabe was ist das Format,
wo ist der Startpunkt, die Länge etc.?

Das Gleiche gilt für meine Abgehende Stimme.

Es würde mich auch interessieren, wie man sms oder ähnliches versenden kann.

Gruss

E
26 Beiträge seit 2004
vor 17 Jahren

Hallo,

Töne aus dem Lautsprecher, das ist so eine Sache. Eine Wave Komponente hast
Du hoffentlich. Die Daten der CAPI kommen praktisch als 8Khz Stream.

Wenn man jetzt z.b. Hören und Sprechen will hat man ein gewisse Verzögerung, abhängig von der Blockgröße die man bei der CAPI Applikation einstellt.
256 Byte ist ein guter Wert wenn man Voice macht. (Je geringer desto besser, aber
mehr Overhead im Gesamtsystem).

Die Daten kommen dann so ca. alle 32ms. Anfangen mit dem Abspielen sollte man nicht unbedingt gleich, weil man ja 2 synchrone Datenströme hat (von ISDN, zum Audio Device) die nicht untereinander synchronisiert sind. Also kann ein kleiner Zwischenbuffer von Vorteil sein.

Das Hauptproblem sind die Daten an sich. Sie liegen im G.711 Format vor. Bei uns in
Mitteleuropa in G.711 ALaw. Diese müßen jetzt nach PCM Wave Mono Khz 16Bit gewandelt werden. Die sie aus dem Chips noch bitinvertiert rauskommen ist zusätzlich noch eine Tabelle vorzusetzen.

G.711 Codecs sind im Windows System vorhanden. Das ACM System dafür ist aber relativ umständlich (halt Microsoft) zu bedienen und eigentlich zu oversized dafür.
Mann kann das durch simple Tabellen machen, dabei werden beide notwendigen Transformationen zu einer zusammengefaßt.

Ich habe sowas irgendwo rumzuliegen, war aber bis lang zu faul es anzuwenden weil mein ACM Code seit Jahren läuft.

Ich werde mal rumsuchen, um was zum posten bereitzustellen.

elli

194 Beiträge seit 2006
vor 17 Jahren

Hab grad keine Enwicklungsumgebung zur Verfügung.
Werde mich melden sobald ich das ausprobieren kann.

Gruss

H
240 Beiträge seit 2006
vor 17 Jahren

Hallo mal wieder...!

Habe länger nicht hier reingeschaut, weil ich doch ziemlich mit meinem Programm beschäftigt war und das Ganze auch etwas anders programmiere, da ich nur eine abgehende Verbindung benötige, also keinen kompletten Wrapper... Jetzt habe ich allerdings ein Problem, bei dem ich einfach überhaupt nicht weiter komme und hoffe, dass mir jemand helfen kann (z.B. elli mit seiner jahrelangen CAPI-Erfahrung gegenüber mir Anfänger...)
Also... mein Programm soll, wenn es fertig ist, über eine ISDN-Karte eine Verbindung zu einem Server aufbauen, über den SMS verschickt werden können. (Solche Server gibt es für jeden zugänglich eigentlich von jedem Mobilfunkanbieter.)
Für mich gibt es jetzt 2 Zielserver, den öffentlichen von T-Mobile und einen nicht öffentlichen von T-Mobile, auf den man nur mit bestimmten Rufnummern zugreifen kann. Im Grunde laufen auf den Servern die gleichen Systeme, auch wenn der nicht öffentliche ein wenig abgeändert wurde, um ihn seinem speziellen Zweck anzupassen.

Mit dem nicht öffentlichen läuft mein Programm einwandfrei (so weit ich es ausprogrammiert hab), ich kann die Verbindung herstellen, mich einloggen, dem System eine SMS übergeben, bekomme meine ID zurück, mich ausloggen und die Verbindung trennen. Ein paar Sekunden später kommt die SMS dann auf meinem Handy an.

Wenn ich das mit dem öffentlichen Server probiere (die selben Methoden, nur eine andere Rufnummer) dann kann ich diesem auch eine SMS übergeben und diese kommt einige Sekunden später bei mir an.
Mein Problem: ab diesem Zeitpunkt sagt meine CAPI einfach mal keinen einzigen Ton mehr, die Verbindung betreffend.
Das letzte was ich von ihr bekomme, ist eine Data_B3_Confirmation mit Info==OK.
Da ich danach eine Nachricht des Servers erwarte (er muss mir ID und Status der SMS schicken) benutze ich CAPI_WAIT_FOR_SIGNAL und da hängt sich das Programm dann auf, weil nichts mehr zurückkommt.
Was mich dabei erst richtig verwirrt ist, dass der Server nach ein paar Sekunden inaktivität die Verbindung beendet aber ich auch keine Disconnect_(B3_)Indication oder sonstwas bekomme...

Wenn ich allerdings an der Stelle, anstatt auf die Nachricht zu warten, ein Listen_Request sende und dann die Antwort abhole, bekomme ich wie gewohnt eine Listen_Confirmation, danach dann aber auch wieder nichts...

Schicke ich dem Server eine Fehlerhafte SMS, mit einer falschen Checksum z.B., schickt mir der Server eine "Message rejected..." Nachricht, die ich auch empfange. Es funktioniert nur nicht, wenn ich alles richtig mache...

Vielleicht hatte ja schonmal jemand den Fehler oder hat davon gehört? Bin für jeden Hinweis dankbar, da mir inzwischen auch die Ansätze zur Fehlerbehebung ausgegangen sind...

MfG hulkstar

MfG hulkstar

E
26 Beiträge seit 2004
vor 17 Jahren

@hulkstar,

nur mal so, CAPI_WAIT_FOR_SIGNAL würde ich ständig in einem Thread laufen lassen
und wenn WAIT_FOR_SIGNAL beendet ist 2 mal CAPI_GET_MESSAGE aufrufen.
Der zweite Aufruf falls noch was da ist. (in der Regel ja nicht). Da wenn eine Nachricht
da ist , dispatcht Du sie. Es können ja nur Indications oder Confirmations kommen.

Wenn Du alles beendet machst Du ja CAPI_RELEASE, das beendet normalerweise
WAIT_FOR_SIGNAL und Du kommts sauber raus aus dem Thread.

Anwendung aufhängen weil von der CAPI nichts mehr kommt ist Quatsch.
CAPI ist Kommunikation und damit asynchron, ankommende Messages immer in einem Thread.

Wenn Du laut Deinem Protokoll jeztzt nach dem Senden einer Nachricht an einem SMS Server eine Antwort erwartest, muß ja ein DATA_B3_INDICATION kommen.

Kann es sein das der Server ein anderes "Protokoll" spricht ?. Ich habe mich um SMS nie gekümmert.

Was mich dabei erst richtig verwirrt ist, dass der Server nach ein paar Sekunden inaktivität die Verbindung beendet aber ich auch keine Disconnect_(B3_)Indication oder sonstwas bekomme...

Das geht nicht. Wenn der Server die Verbindung abbaut, spricht auf dem D-Kanal muß die CAPI Implementation Dir zuerst ein DICONNECT B3 INDICATION und danach ein DISCONNECT INDICATION senden. Alles andere verstößt gegen die Regeln. Da die meisten eine Fritz CARD haben ist das auch so. Wenn Du in diesem Zustand die ISDN Leitung mal abziehst mußt Du auch einen DISC_B3_IND und dann ein DISC_IND mit Grund 0x3301 bekommen.

Als erstes würde ich Dir empfehlen Dein Programmlogik zu ändern. Mann kann keine Nachricht an die CAPI senden und dann mal auf eine Anwort der CAPI warten, das ist schlechte Programmierung an dieser Stelle. Alles wird durch States und Ereignisse gesteuert. Ein Ereignis ist hier, das eine neue CAPI Nachricht da ist, die abhängig vom Verbindungszustand eine neue Aktion bzw. Zustandswechsel bewirkt. Hast Du eine Verbindung löst der Inhalt der Daten einen neuen Zustandsautomaten deiner Anwendung aus.

Wenn Du Fritzt hast, hat da nicht mal sowas wie ein CAPI Trace existiert ? Ich hatte auch mal eine Löungs da habe ich eine Trace DLL in eine Fremd CAPI injeziert. Weiß aber nicht mehr ob das noch geht.

elli

PS. Stück Source Code habe ich noch gefunden. Es ist AS IS, denn ich habe den C# Pfad nicht weiterverfolgt ....

H
240 Beiträge seit 2006
vor 17 Jahren

Hallo elli,

erstmal Danke für die Antwort!

Meinen Fehler habe ich inzwischen gefunden, bzw. meinem Betreuer ist es aufgefallen, als er sich die Traces angeschaut hat (seine eigenen Fehler findet man ja nur selten...):
Ich hatte vergessen, auf die DATA_B3_INDICATIONs mit DATA_B3_RESPONSEs zu antworten.
So, und jetzt kannst du mich ruhig steinigen, ich habs verdient... X(

Zu dem extra Thread: In dem Programm geht es nicht unbedingt darum, alles richtig zu programmieren, sondern darum, dass es immer das gleich tut und das möglichst stabil. Ausserdem habe ich nur begrenzt Zeit, weil es sich um ein Praxissemester handelt. Ob das jetzt schlechte Programmierung ist, ist erstmal nicht wichtig...

MfG hulkstar

E
26 Beiträge seit 2004
vor 17 Jahren

@hulkstar

hallo,

ch hatte vergessen, auf die DATA_B3_INDICATIONs mit DATA_B3_RESPONSEs zu antworten.

Naja ein paar mal kann man das machen, zumindest einmal weniger als man bei
CAPI_REGISTER und B3 Block Count angegeben hat. Dann noch ein Block und dann ist Sense 🙂

Aber ein DISC_B3_IND bzw. DISC_IND hat damit nix zu tun. Das muß auch so kommen. Das würde mich mal interessieren was passiert wenn Du wieder keine
DATA_B3_RESP schickst und dann den ISDN Stecker ziehst.

elli

H
240 Beiträge seit 2006
vor 17 Jahren

Ja, du hast recht, wenn ich dann den Stecker ziehe, bekomme ich erst eine DISC_B3_IND und dann eine DISC_IND.
Allerdings bekomme ich die nicht, wenn die Gegenseite die Verbindung trennt.

MfG hulkstar

194 Beiträge seit 2006
vor 17 Jahren

Hallo Zusammen
Ich habe mir auf Ratschalg von elli jetzt ein bisschen im Netz über ALAW gesucht

//EDIT:

Um ein bisschen ordnung zu schaffen habe ich vorherige Tabelle gelöscht


class AlawConverter
    {
        public AlawConverter()
        {
         }

        public byte[] SND_2_WAV(byte[] Data)
        {
            for (int i = 0; i < Data.Length; i++)
            {
                Data[i] = _tableAlaw[(int)Data[i]];
            }

            return Data;
        }
        private byte[] _tableALAW =
{0x06A, 0x095, 0x07E, 0x081, 0x02A, 0x0D6, 0x07A, 0x085, 0x075, 0x08A, 0x07F, 0x080, 0x055, 0x0AB, 0x07D, 0x082, 0x062, 0x09D, 0x07E, 0x081, 0x00A, 0x0F6, 0x078, 0x087,
0x071, 0x08E, 0x07F, 0x080, 0x045, 0x0BB, 0x07C, 0x083, 0x06E, 0x091, 0x07E, 0x081, 0x03A, 0x0C6, 0x07B, 0x084, 0x077, 0x088, 0x07F, 0x080, 0x05D, 0x0A3, 0x07D, 0x082, 0x066, 0x099,
0x07E, 0x081, 0x01A, 0x0E6, 0x079, 0x086, 0x073, 0x08C, 0x07F, 0x080, 0x04D, 0x0B3, 0x07C, 0x083, 0x068, 0x097, 0x07E, 0x081, 0x022, 0x0DE, 0x07A, 0x085, 0x074, 0x08B, 0x07F, 0x080,
0x051, 0x0AF, 0x07D, 0x082, 0x060, 0x09F, 0x07E, 0x081, 0x002, 0x0FE, 0x078, 0x087, 0x070, 0x08F, 0x07F, 0x080, 0x041, 0x0BF, 0x07C, 0x083, 0x06C, 0x093, 0x07E, 0x081, 0x032, 0x0CE,
0x07B, 0x084, 0x076, 0x089, 0x07F, 0x080, 0x059, 0x0A7, 0x07D, 0x082, 0x064, 0x09B, 0x07E, 0x081, 0x012, 0x0EE, 0x079, 0x086, 0x072, 0x08D, 0x07F, 0x080, 0x049, 0x0B7, 0x07C, 0x083,
0x06B, 0x094, 0x07E, 0x081, 0x02E, 0x0D2, 0x07A, 0x085, 0x075, 0x08A, 0x07F, 0x080, 0x057, 0x0A9, 0x07D, 0x082, 0x063, 0x09C, 0x07E, 0x081, 0x00E, 0x0F2, 0x078, 0x087, 0x071, 0x08E,
0x07F, 0x080, 0x047, 0x0B9, 0x07C, 0x083, 0x06F, 0x090, 0x07E, 0x081, 0x03E, 0x0C2, 0x07B, 0x084, 0x077, 0x088, 0x07F, 0x080, 0x05F, 0x0A1, 0x07D, 0x082, 0x067, 0x098, 0x07E, 0x081,
0x01E, 0x0E2, 0x079, 0x086, 0x073, 0x08C, 0x07F, 0x080, 0x04F, 0x0B1, 0x07C, 0x083, 0x069, 0x096, 0x07E, 0x081, 0x026, 0x0DA, 0x07A, 0x085, 0x074, 0x08B, 0x07F, 0x080, 0x053, 0x0AD,
0x07D, 0x082, 0x061, 0x09E, 0x07E, 0x081, 0x006, 0x0FA, 0x078, 0x087, 0x070, 0x08F, 0x07F, 0x080, 0x043, 0x0BD, 0x07C, 0x083, 0x06D, 0x092, 0x07E, 0x081, 0x036, 0x0CA, 0x07B, 0x084,
0x076, 0x089, 0x07F, 0x080, 0x05B, 0x0A5, 0x07D, 0x082, 0x065, 0x09A, 0x07E, 0x081, 0x016, 0x0EA, 0x079, 0x086, 0x072, 0x08D, 0x07F, 0x080, 0x04B, 0x0B5, 0x07C, 0x083};
     }
// habe ich im Netz gefunden und angepasst


Ich denke das sollte mit wenigen Anpassungen brauchbar sein. Was ich nicht rausgefunden habe ist wie ich die verschiedenen modes Abfrage um die korrekte konvertierung vorzunehmen.

Da ich ausserdem sehr wenig ISDN Kenntnisse habe stellt sich für mich die Frage:
Wie komme ich an die Datren im B3_Data_ind ?

Gemäss CAPI dokumentation ist dieser ein 32-Bit pointer.
Einfach aus dem Bytestream auslesen geht ja nicht.
Also überprüfe ich die CapiMessage gleich im Konstruktor auf B3_data_ind
und haole aus dem Pointer einen neuen Pointer, welcher dann in ein ByteArray
kopiert wird.



   private byte[] b3Data;

   public CapiMessage( IntPtr msg)
   {
    // Save unmanged message buffer position
    streamBuffer   = new byte[2048 + 32 ];

    Marshal.Copy( msg, streamBuffer,0,8);


    length  = readWord(0);
    command = (CapiCommand) (readWord(4) & 0xFFFF);

    // check for DATA B3 Indication
    if (command == CapiCommand.cmDataB3Indication)
    {
        IntPtr DataRaw =Marshal.AllocHGlobal(256);
        /* byte DataLenght =1; word CapiCommand= 2, dword Datahandle = 4
      also: 1+2+4=7*/
        DataRaw = Marshal.ReadIntPtr(msg, 7);//<--7
        Marshal.Copy(DataRaw, b3Data, 0, 256);
        Marshal.FreeHGlobal( DataRaw);
    }

Nun das Array scheint tatsächlich Daten zu beinhalten, ich weiss nur nicht ob es die sind, die ich will.
Wäre in diesem Moment um jede Hilfe dankbar.

Gruss

Balaban_S

H
240 Beiträge seit 2006
vor 17 Jahren
verbundene Nummer?

Hallo,

ich bin mit meinem Programm jetzt schon etwas weiter gekommen und es macht mir schon fast wieder spass, daran rumzuprogrammieren. (Nach ein paar dämlichen Fehlern, die mich wochenlang aufgehalten haben...)

Ich Moment häng ich an folgendem Problem: Gibt es eine Möglichkeit, herauszufinden, mit welcher Nummer die CAPI verbunden ist?

Hintergrund ist der folgende:
Ich nehme SMS einer Clientapplication über einen Webservice an. In dieser SMS ist ein Flag, das bestimmt, über welchen Server (öffentlich oder speziell) die Nachricht verschickt werden soll.
Das Programm ist so ausgelegt, dass es, falls schon eine Verbindung besteht, die neue SMS einfach hinterherschiebt und sonst eine neue Verbindung aufbaut.
Und genau da wird es schwierig, weil ich ja wissen muss, zu welchem Server im Moment eine Verbindung besteht und eventuell noch eine zweite Verbindung aufbauen muss...

Hat da jemand eine Idee, wie ich das lösen kann?
Wäre für jede Anregung sehr dankbar!
hulkstar

MfG hulkstar

tom-essen Themenstarter:in
1.820 Beiträge seit 2005
vor 17 Jahren

Hallo!

Bin mir nicht sicher, aber zumindest IsdnWatch zeigt doch die Nummern von gerade belegten Kanälen an, oder? Es sollte somit möglich sein.
Ansonsten könntest du evtl. in deinen SMS-Client einprogrammieren, dass er z.B. in einer XML-Datei oder einfach nur im Speicher vermerkt, ob und zu welcher Nummer gerade eine Verbindung besteht.

Nobody is perfect. I'm sad, i'm not nobody 🙁

H
240 Beiträge seit 2006
vor 17 Jahren

N'abend!

Die Idee hatte ich auch schon, werd mich da morgen wohl auch ransetzen. Aber eine direkte Abfragemöglichkeit wäre mir natürlich lieber gewesen...

Schönen Abend noch!
hulkstar

MfG hulkstar

194 Beiträge seit 2006
vor 17 Jahren

Hallo

@hulkstar
Wie kommst denn du an die b3-daten ?

Gruss

Balaban_s

H
240 Beiträge seit 2006
vor 17 Jahren

Hallo Balaban_S,

Dein Ansatz müsste schon fast funktionieren. Hier ist ein Auszug aus meinem Code


                // Daten extrahieren, cm ist die CapiMessage (Data_B3_Indication)
                IntPtr dataPtr = (IntPtr)cm.readDWord(12);
                // Länge der Daten
                int len = cm.readWord(16);
                // Data Handle
                int handle = cm.readWord(18);
                // Buffer für die Daten
                byte[] buffer = new byte[4096];
                // Daten in Buffer kopieren
                Marshal.Copy(dataPtr, buffer, 0, len);
                // Speicher freigeben
                dataPtr = IntPtr.Zero;

In der Data_B3_Indication ist wie gesagt der Pointer auf die Daten. Die Adresse wird mit .readDWord(12) aus der Message ausgelesen.

Die Länge der Daten im Speicher stehen in den Bytes 16 und 17, kann also mit readWord() in ein int ausgelesen werden.

Dann noch schnell mit den gesammelten Daten in ein Bytearray kopieren. Fertig.

Den Handle brauchst Du für die Data_B3_Response.

So sollte es dann funktionieren.

MfG hulkstar

E
26 Beiträge seit 2004
vor 17 Jahren

Hallo,

@hulkstar
die Nummer mit der ihr verbunden seit wird bei CON_ACT_IND angezeigt. Von außen
kommt man aber nicht mehr ran an eine CAPI. Hast Du die Kontrolle über die Verbindung ?, wenn nicht mußt Du sowiso eine neue Verbindung aufbauen. Male mal
eine kleine Skizze, denn so ganz habe ich die Architektur nicht kapiert.

Kleiner Tip am Rande, ich würde nie bei jeder DATA_B3_IND einen neuen Buffer
mit new allozieren. Baut euch eine Resource Queue mit der Anzahl von freien
Buffern die Ihr bei CAPI_REGISTER angegeben habt. Die maximale Größe eines dieser Buffer habt ihr bereits bei CAPI_REGISTER festgesetzt.

Wenn ein DATA_B3_IND Nachricht bearbeitet wird, freien Buffer Block ausqueuen.
Daten kopieren und zur Verarbeitung schicken. Wenn DATA_B3_RESP gemacht wird
diese Buffer wieder zurück an die Resourcequeue.

Bei Kommunikation treten viele asynchrone Ereignisse auf. Ich halte es für einen schlechten Stil und da es führt zu nicht deterministischen Laufzeitverhalten wenn
immer fleißig mit new gearbeitet wird.

@Balaban_S

if (command == CapiCommand.cmDataB3Indication)  
{  
    IntPtr DataRaw =Marshal.AllocHGlobal(256);  
    /* byte DataLenght =1; word CapiCommand= 2, dword Datahandle = 4  
  also: 1+2+4=7*/  
    DataRaw = Marshal.ReadIntPtr(msg, 7);//&lt;--7  
    Marshal.Copy(DataRaw, b3Data, 0, 256);  
    Marshal.FreeHGlobal( DataRaw);

Die Datenlänge sind immer 2 Byte.
und mit diese Länge dann das Copy.

Du hast bereits einen unmanged Pointer, also ist MarshallhGlobal() falsch und überflüßig. Das braucst Du beim DATA_B3_REQ.

Der Code von hulkstar ist fast korrekt, man braucht keine 4096 Byte allozieren,
sondern nur von der Größe 'len'.

Viel Spaß weiterhin 🙂

elli

194 Beiträge seit 2006
vor 17 Jahren

@elli OK Das IntPtr MAnagament muss noch ausgefeilt werden gebe ich zu.

Aber zur Problemlösung:

  1. Also wie gesagt ist ISDN-Programmierung nicht meine Stärke deshalb möchte ich meinem früheren Post betreffend ALAW -Converter widersprechen und das hier angeben:

//EDIT: siehe oben
2.Was die b3-betrifft: so habe ich nach Hulkstars Referenz gehandelt und folgendes
implementiert:


   public CapiMessage( IntPtr msg)
   {
    streamBuffer   = new byte[2048 + 32 ];
    Marshal.Copy( msg, streamBuffer,0,8);
    length  = readWord(0);
    command = (CapiCommand) (readWord(4) & 0xFFFF);

    // check for DATA B3 Indication
    if (command == CapiCommand.cmDataB3Indication)
    {
        IntPtr DataRaw = Marshal.ReadIntPtr(msg, 12);
        //int len = Marshal.ReadInt32(msg, 16);
        _B3DataHandle = (ushort)Marshal.ReadInt16(msg, 18); 
        b3Data = new byte[1024];// Die Länge sollte man nicht hart codieren
        Marshal.Copy(DataRaw, b3Data, 0, 1024);
        DataRaw = IntPtr.Zero;// gute Lösung
    }


  1. Wenn ich nun diese Komponente von Codeproject.com

in Verbindung mit dem ALAW-Converter folgenderweise in der Klasse
Isdn-(bzw. Voice-)Connection
von elli einbinde:


  private WaveLib.WaveOutPlayer m_Player;
  private WaveLib.FifoStream m_Fifo = new WaveLib.FifoStream();
  private byte[] m_PlayBuffer;
  private AlawConverter alawConverter;

//im Konstruktor:
   private void Start()
  {
      alawConverter = new AlawConverter();
      Stop();
      try
      {
          WaveLib.WaveFormat fmt = new WaveLib.WaveFormat(8000, 8, 1);
          m_Player = new WaveLib.WaveOutPlayer(-1, fmt, 1024, 4, new WaveLib.BufferFillEventHandler(Filler));
       }
      catch
      {
          Stop();
          throw;
      }
  }

//im destruktor:
  private void Stop()
  {
      if (m_Player != null)
          try
          {
              m_Player.Dispose();
          }
          finally
          {
              m_Player = null;
          }
      m_Fifo.Flush(); // clear all pending data
  }



und dann schlussendlich bei dem betreffenden Event



  protected virtual void onDataB3Indication(CapiMessage message) 
  {
      
      if (message.B3Data == null)
      {
          throw new NullReferenceException("");
      }
      else
      {
          m_Fifo.Write(alawConverter.SND_2_WAV(message.B3Data), 0, 1024);         
          capiResponse.dataB3Response(message.Ncci, message.B3DataHandle);

      }
  }

in die Waveausagabe schreibe, wird es sicher einige freuen zu hören, dass ich eine fast hellklare Ausgabe aus dem Lautsprecher kriege.
Es ruckelt nur wenig man; man kann das sicher noch optimieren.

Die Frage bleibt wie sende ich ein DataB3Req um ein komplettes Gespräch führen zu können. Die Komprimierung in das ALAW- Format scheint ein bisschen komplizierter zu sein als zurück in WAV.

Würde mich sehr freuen wenn jemand noch rauskriegen könnte, wie man den nun Ton Rein kriegt.

Gruss

Balaban_S

E
26 Beiträge seit 2004
vor 17 Jahren

Hallo,

@balaban_s

Also Codeproject hast Du ja schon entdeckt, dann hätte Dir ein wenig mehr weitersuchen auch geholfen. Der PCM -> A-Law Codec muß nun einen Dynamic
Bereich von 0xFFFF Werten zu 255 Werten abbilden. Kann man natürlich mit
Tabellen machen, aber der Artikel G.711
wird Dir da sicherlich helfen.

Wenn Du also ankommende WAVE IN Daten nach A-Law Konvertiert hast, müßen sie noch mit der Tabelle die ich schon mal gepostet hatte (oder nicht ?) bitinvertiert werden. Dann können sie raus mit DATA B3 REQUEST.

Das ruckeln beim Abbspielen bekommst Du nur weg wenn Du ein wenig Latency und
damit Echo erzeugst und das abspielen erst anfängst wenn Du den 2. oder 3. Block empfangen hast. Beide Datenströme arbeiten mit 8Kz sind aber nicht synchronisiert. Mit diesem kleinen Vorlaufbuffer kannst Du eventuelle Differenzen überleben. Andere Methode wäre einen kleinen Silent Block abzuspielen. Das kann Dich aber alles nicht gegen Windows schützen. Da es kein Realtime System ist, kann es passieren das Du für 100ms und mehr im Usermode keine Rechenzeit bekommst. Dann ist nicht mehr mit einem kontinuirlichen Datenstrom. (Ich habe es erlebt als ich 8 ISDN Kanäle gebündelt haben und eine Applikation alle 10 ms die entsprechenden Daten liefern muß).

Je kleiner die Datenblockgröße wird, umso geringer ist das Echo das Du auf der Leitung hast.

Ich hoffe das hilft weiter. Wenn nicht, noch mal nachfragen.

elli

194 Beiträge seit 2006
vor 17 Jahren

@elli

Danke nochmals. Den Artikel zu g711 hatte ich schon gesehen.
Jedoch scheint dieser nicht auf Anhieb zu funktionieren(zumindest nicht auf meinem System).

Weder der **AlawDecoder **noch **AlawEncoder **haben bei mir das gewünschte Ergebnis geliefert.

Ich glaube da fehlen die erwähnten Tabellen. Wo finde ich denn welche ?

Gruss

Balaban_S

E
26 Beiträge seit 2004
vor 17 Jahren

Hallo,

also nochmal die Logik. Jedes Byte von der ISDN Leitung (sprich CAPI) bzw. muß erst
über z.b. eine Tabelle bit inevrtiert werden. Anbei die Tabelle, bzw. wie man sie
aufbaut (geht besser).

 BYTE  lookUpTable[256];

  int  b;
  BYTE o;

  for ( b = 0; b <= 255; b++ )
   {
    o = 0;
    if ( b & 0x01) o |= 0x80;
    if ( b & 0x02) o |= 0x40;
    if ( b & 0x04) o |= 0x20;
    if ( b & 0x08) o |= 0x10;
    if ( b & 0x10) o |= 0x08;
    if ( b & 0x20) o |= 0x04;
    if ( b & 0x40) o |= 0x02;
    if ( b & 0x80) o |= 0x01;

    lookUpTable[b] = o;
   }

Also für jedes BYTE von DATA_B3_IND genannt b ist i = lookupTable**.
i wird dann von A-Law nach Wave konvertiert.

Diese Konvertierung kann durch eine Tabelle erfolgen da wir nur 255 Werte haben.

Umgekehrt Senden von PCM Daten, erfolgt zuerst die PCM -> A-LAW konvertierung.
Jedes BYTE b das da rauskommt wid jetzt zu j = lookupTables**.
j geht dann als Byte beim DATA_B3_REQ raus.

Das reine Anwenden des G.711 Algos kann nie zu einem hörbaren Erlebnis bei ISDN werden. Es gibt Linux Sourcen die das besser verdeutlichen.

elli

//EDIT: c#-code-tags ergänzt

194 Beiträge seit 2006
vor 17 Jahren

Hallo,

Dein Algo ergibt bei mir folgende Tabelle:


//um bei c# zu bleiben
byte[] lookuptable={
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0,
0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4,
0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC,
0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA,
0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6,
0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1,
0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9,
0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD,
0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3,
0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7,
0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF,
0x3F, 0xBF, 0x7F, 0xFF
}


Ich habe es mitterweile geschafft eine alaw codierte Wave-Datei (mit einem Tool konvertiert und dann Tabelle davorgesetzt) per Data_b3_request zu senden.

Aber was mir nicht gelungen ist, ist es die Mikrophonausgabe direkt an die CAPI zu senden bzw. die Encoder funktionieren nicht.

Keine Ahnhung warum.

PS: An die Admins: sollte es evtl ein Problem sein dass ich andauernd Tabellen poste, bitte nur sagen und ich wede in Zukunft als Anhang posten

Gruss

Balaban_S

H
240 Beiträge seit 2006
vor 17 Jahren
[gelöst] argh...!

Hallo,

ich melde mich auch mal wieder zu Wort und das natürlich, weil ich Hilfe brauche.

Ich habe meine Anwendung soweit eigentlich fertig. Habe das Ganze in ein Setup-Projekt gepackt und wollte es auf einem anderen Rechner testen...

Entwicklungsrechner: WinXP Prof.; AVM Fritz-Card.
Testrechner: Win2000; BinTec Bianca/Brick-XS (Wer sie nicht kennt: die Brick ist ein Router, der auch als ISDN-Karte genutzt werden kann und wird über Lan angesprochen bzw. die CAPI spricht ihn über LAN an).

Auf meinem Entwicklungsrechner läuft alles einwandfrei.

Auf dem Testrechner konnte ich 1 mal eine SMS mit meinem Programm versenden, danach ging es nicht mehr.
Bei jedem Anwahlversuch bekomme ich mit der Connect Confirmation "Info = 0x2003 (No PLCI available)".
Das passiert auch, wenn ich den Rechner ganz frisch gestartet habe und mein Programm noch nicht lief. So weit ich weiss, benutzt auch kein anderes Programm die ISDN-Karte, weil, sie wurde extra zum Testen da angeschlossen.

Noch ein wenig zu meinem Programm:
Ich benutze 2 Instanzen meines CapiWrappers um Verbindungen mit 2 verschiedenen Servern aufzubauen. in dem Moment, in dem ich teste stellt nur eine der Instanzen eine Verbindung her. Das ist aus den Logs ersichtlich.
Auf dem Entwicklungsrechner läuft das ganze auch, wie gesagt, super.

Liegt das jetzt an Windows2000 oder an der BinTec CAPI?
Ich habe leider keinen anderen Rechner hier, mit dem ich sonst testen könnte...

Ich bin für jeden Hinweis sehr dankbar!
MfG hulkstar

P.S.: Ich habe in einer Newsgroup jemanden mit einem ähnlichen Problem gefunden, der Eintrag ist aber leider aus 2005 und ich habe bisher keine Antwort auf meine Anfrage bekommen.
Er bekommt den Fehler allerdings schon, wenn er versucht, sich ein weiteres Mal bei der CAPI zu registrieren...
http://mailgate.supereva.com/comp/comp.dcom.isdn.capi/msg04385.html

Lösung:
Es handelte sich um eine alte Remote-CAPI von BinTec. (V 4.1, aktuell ist 7.1 Rev.14; da konnt ich aber nix für, wurd mir so gegeben...)
Das Problem dabei war, dass die Konfiguration dieser Remote-CAPI die Einstellungen user-spezifisch für den User, der sie einrichtet, in der Registry gespeichert hat.
Da mein Service aber unter einem anderen Account lief, konnte die CAPI dann nicht auf diese Einstellungen zugreifen.
Die neue Version hats dann halt gelöst, da gibt es eine Option, damit die Einstellungen global gespeichert werden.

MfG hulkstar

E
26 Beiträge seit 2004
vor 17 Jahren

@hulkstar

hallo,

der plci ist eine Resource die beim D-Kanal Verbindungsaubau, sprich beim Connect Request oder Connect Indication vergeben wird. Wenn noch kein Verbindungaufbau erfolgte, muß ein PLCI immer zu Verfügung stehen. Ist das nicht der Fall, kann es sich nur um ein Problem der Capi Implmentierung handeln oder wie bei Dir anscheinend um ein Konfigurationsproblem des Capi Servers.

Da Du anscheinend schon eine Verbindung augebaut hattest, schreibe mal die Sequence der Capi Befehle auf die Du beim Verbindungsabbau machst.

elli

ps. ein nicht erfolgter disconnect response auf ein disconnect indication oder dabei eine fasche Angabe verhindern das Freigeben des PLCIs.

H
240 Beiträge seit 2006
vor 17 Jahren

Hallo elli,

danke für deine Antwort aber das Problem ist schon gelöst. Es lag nur an der Konfiguration der Remote-CAPI von BinTec. Meine Verbindungsauf- und Abbaus sind in Ordnung (behaupte ich jetzt einfach mal).

MfG hulkstar

1.457 Beiträge seit 2004
vor 17 Jahren

Hallo,

Ich bin relativ neu in der CAPI Programmierung und würde gerne für mein Projekt einen Fax-Client der Mithilfe einer Netzwerk CAPI Faxe versenden und empfangen kann entwickeln.

Da es mir scheint, dass nur Teile des Wrappers und nicht der letzte Stand gepostet wurde, wollte ich fragen ob Ihr dies überhaupt zum Download zu Verfügung stellt / stellen könntet und ob ich es in meinem Projekt benutzen darf?

Vielen Dank im Voraus.

tom-essen Themenstarter:in
1.820 Beiträge seit 2005
vor 15 Jahren

Hallo!

Da ich z.Zt. wieder aktiv an dem Thema arbeite, wollte ich mal meine derzeitigen Erkenntnisse zur Diskussion freigeben.

Prinzipiell läuft das Ganze folgendermaßen ab:1.Feststellen, ob CAPI verfügbar ist 1.Beim CAPI-Treiber als CAPI-Anwendung registrieren 1.Zur Benachrichtung von CAPI-Nachrichten registrieren 1.Informationen zur CAPI-Hardware abrufen

Beim Beenden der Anwendung muss sichergestellt werden, dass die Anwendung wieder komplett vom CAPI abgemeldet wird.

Bzgl. der Nachrichten gibt es 4 verschiedene Typen:*Anwendungen senden immer REQUESTS und erhalten CONFIRMATIONS *CAPI sendet immer INDICATIONS und "erwartet" RESPONSES

Edit: Wie in einigen Threads festzustellen ist, werden CAPI-Anwendungen oft nur zu einem bestimmten Zweck geschrieben. Dazu werden dann auch oft nur die nötigsten CAPI-Nachrichten berücksichtigt und teilweise in einer bestimmten Reihenfolge "erwartet".

EDIT: Dies ist jedoch ein fataler Fehler. Jede Nachricht, die vom CAPI-System an die Anwendung geschickt wird, muss entsprechend behandelt UND beantwortet werden, ansonsten läuft die gesamte CAPI-Kommunikation aus dem Ruder.

Prinzipiell sollte man dazu in CAPI-Anwendungen den Empfang von Nachrichten in einen eigenen Prozess auslagern, welcher bei neuen Nachrichten ein Event auslöst. In diesem können die "erwarteten" Nachrichten entsprechend behandelt werden.

Das sind erstmal die absoluten Basics, genaueres folgt demnächst.

Nobody is perfect. I'm sad, i'm not nobody 🙁

E
26 Beiträge seit 2004
vor 15 Jahren

Hallo,

lustig das es weiter geht.

Prinzipiell sollte man bei CAPI-Anwendungen den Empfang von Nachrichten in einen eigenen Prozess auslagern, welcher bei neuen Nachrichten ein Event auslöst. In diesem können die "erwarteten" Nachrichten entsprechend behandelt werden.

Thread ist schon mal OK. In dem kann Du CAPI_WAIT_FOR_SIGNAL machen.
Es gibt keine erwarteten Nachrichten ! Prinzipiel müßen die State Machine alles
abfangen und je nach dem was Du als CAPI Nachricht gesendet hast und was Du erreichen willst entsprechend reagieren. Das hat bei mir auch etliche Zeit gedauert
und es gibt diverse unterschiedliche Implementierungen. Dann kann es durch Race Conditions auch noch zu lustigsten Effekten kommen. Erst heute würde ich sagen ich fange eigentlich alles ab was so kommt.

Aus Sicht einer API gibt es wirklich nur eine Reihe von Events die eine Applikation interessieren.

Frage ruhig, ich stehe bereit 🙂

elli

tom-essen Themenstarter:in
1.820 Beiträge seit 2005
vor 15 Jahren

Hallo elli!

Mit den erwarteten Nachrichten meinte ich ja eigentlich auch, dass andere dass oft falsch machen. Dann soll z.B. nur die Nummer eines eingehenden Anrufes erkannt werden, die anderen Nachrichten werden nicht beachtet. Nur wundern sich die Leute dann, dass es meist nur beim ersten Anruf funktioniert.

Hab' dies in meinem letzten Beitrag nochmal nachgebessert. Und wenn ich Fragen habe, komme ich selbstverständlich auf dein Angebot zurück 😉

Nobody is perfect. I'm sad, i'm not nobody 🙁

M
1 Beiträge seit 2008
vor 15 Jahren

Hallo,

ich bin neu im Bereich CAPI. Ich bräuchte Hilfe über die Machbarkeit von folgendem Problem:

Vorhanden ist eine rel. alte Siemens HiPath Anlage. An dieser Anlage sind über ISDN Siemens AC-Win Vermittlungskonsolen angeschlossen. Siemens verwendet dazu anscheinend eine hauseigene ACCapi20.dll. Ich müsste nun ein einfaches Programm schreiben welches Anrufe entgegennimmt und auf eine andere Nummer weiterleitet (die Vermittlungskonsolen kommen weg).

Für die Standard Capi20 dll sollte das nicht so schwierig umzusetzen sein, wenn man einen entsprechenden Wrapper hat. Hat jemand Erfahrung mit ACCapi20.dll?

Gibts von dem c# Wrapper der hier im Thread vorgestellt wurde bereits eine funktionsfähige Funktion wo die beschriebenen Probleme behoben wurden?

  • Markus
tom-essen Themenstarter:in
1.820 Beiträge seit 2005
vor 15 Jahren

Hallo!

@maxibk:
Der Wrapper funktioniert soweit, er dienst ja auch nur dazu, die ISDN-Nachrichten zu senden bzw. zu empfangen.
Mit einer Test-Anwendung kann ich bereits Anrufe anzeigen, annehmen und beenden.

Hersteller von CAPI-kompatiblen Anlagen müssen ja immer eine eigene CAPI-Implementierung mitliefern, die auf Anwenderseite die CAPI-Schnittstelle anbietet und auf der Hardwareseite die Anlage korrekt anspricht. Man sollte also davon ausgehen können, dass die ACCAPI20.dll auch CAPI-kompatibel ist.

Ich werde mal die Test-Anwendung ein wenig ausbauen und dann hier posten.

Nobody is perfect. I'm sad, i'm not nobody 🙁

E
26 Beiträge seit 2004
vor 15 Jahren

Hallo!

@maxibk:

meinst du per CAPI einen Ruf annehmen dann einen zweiten aufbauen und die
die B-Kanäle quais verbinden ?

Zuerst sollte Du die Export Funktionen von accapi20.dll prüfen., ob es überhaupt eine
CAPI 2.0 Implementierung ist.

Muß je eine sehr alte HiPath sein, denn die Software der letzte 6 Jahre kenne ich dachte ich.

>Gibts von dem c# Wrapper

c# ist nicht immer der eleganteste Weg 🙂

elli

B
375 Beiträge seit 2007
vor 15 Jahren

Was muss ich davon kopieren oder downloaden um den Wrapper nutzen zu können? thx

tom-essen Themenstarter:in
1.820 Beiträge seit 2005
vor 15 Jahren

Hallo!

Bin leider z.Zt. berufsbedingt sehr stark eingespannt, von daher hat sich das Ganze etwas verzögert.

Versuche aber gerade, die bisherigen Erkenntnisse in einer DLL und evtl. einigen Beispiel-Projekten.

Wenn jemand schneller etwas braucht, einfach per ICQ nachfragen.

Nobody is perfect. I'm sad, i'm not nobody 🙁

tom-essen Themenstarter:in
1.820 Beiträge seit 2005
vor 15 Jahren

Hallo!

Habe den ersten Beitrag überarbeitet. Dort befindet sich nun ein Archiv mit dem Wrapper und einem einfachen Beispiel-Projekt.

Nobody is perfect. I'm sad, i'm not nobody 🙁

tom-essen Themenstarter:in
1.820 Beiträge seit 2005
vor 15 Jahren
CAPI-Wrapper: Problem bei Verbindungsaufbau

Hallo!

Bin gerade dabei, aufbauend auf dem Wrapper eine Zusatz-Lib mit Kapselung komplexer Vorgänge zu erstellen.

Dabei verzweifle ich gerade an dem Problem, eine ausgehende Verbindung aufzubauen.
Ich sende CONNECT_REQ und erhalte CONNECT_CONF.

Nach kurzer Zeit klingelt der in CONNECT_REQ angegebene Anschluss und ich nehme ab.

Dann erhalte ich CONNECT_ACTIVE_IND und sende CONNECT_ACTIVE_RESP.

Darauf sende ich CONNECT_B3_REQ und erhalte CONNECT_B3_CONF.
Hier allerdings ist Schluß, dass erwartete CONNECT_B3_ACTIVE_IND bleibt aus.

Wenn ich dann auflege, erscheinen die entsprechenden Meldungen.

Kann mir einer sagen, warum ich kein CONNECT_B3_ACTIVE_IND erhalte.
Wenn weitere Informationen benötigt werden (Struktur-Inhalte) einfach sagen.

Nobody is perfect. I'm sad, i'm not nobody 🙁