Translate

2013-07-04

Differences between LeanStartup and Agile



LeanStartup and Agile are incremental software development methods with similarities but a lot of fundamental differences.

Where Agile (philosophy based on Agile manifesto) is used for incrementally developing software projects, LeanStartup is aimed for incrementally building successful startups (of services or products, startup doesn’t necessarily means little company in a garage), discovering successful business models when encountering high uncertainty in the market.

We can say that both of them aim not to develop the wrong product, and if doing so, discover it as fast as possible.

Both of them are not really alternatives for strictly defined large projects (with external constraints or regulations for example) but rather for medium and little sized projects and teams that are able to face and accept frequent requirements changes.

The main key of both methods is the customer feedback (needs to be highly involved in Agile and can even not be known at the beginning of the process in LeanStartup) that will determine;
-       In case of agile; validation of the iteration and definition of the next one, refactoring if necessary.
-       In case of LeanStartup; reaction of customers (using metrics) to the “minimum viable product” will help to decide if to persevere or pivot.

Both methods aim to quickly detect if the project is in the wrong path (or product) and immediately reacts, avoiding waste of resources and money.

With LeanStartup, if you fail you will fail fast and minimize costs of failure.

Both of methods place the human in the center and not the processes; they then require highly skilled developers (not only capable to translate strictly designed system to implementation), capable to work on quick and small batches with interleaved development process.

If we wanted to summarize these both methods tries to achieve quality and customer satisfaction faster and cheaper as possible. They are not concurrent methods but rather used in different contexts.

2013-04-30

C# script to popup customer record linked to the incoming caller id

Here is the C # code of a small module that I wrote, which connects to the Asterisk manager then detects whether the agent on the machine has answered a call and starts a url accordingly.

Very useful to pop up the customer record linked to the incoming caller id.

Source code;

config.xml



Mainform.cs

/*
* SAMUEL LEVY
* Date: 21/01/2007
* Time: 11:23
* monte_fiche.cs
* Detect "link" event in the manager, detects the incoming caller id, then launch the url of the 

* customer record
*/
using System.Net;
using System.Xml;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Text;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.Win32;

namespace monte_fiche
{

// SL 21/01/2007
class manager_access
{
private string IPadress;
private int port;
private string utilisateur;
private string password;
private string sipuser;
private string scriptmontee;
private Socket MyAstSocket;
private IPEndPoint MyAstServerEndPoint;

//constructeur par defaut
public manager_access()
{
this.IPadress="";
this.port=0;
this.utilisateur="";
this.password="";
this.sipuser="";
this.scriptmontee="";
}

//constructeur
public manager_access(string t_server,int t_port,string t_utilisateur,
string t_password,string t_sipuser,string t_scriptmontee)
{
this.IPadress=t_server;
this.port=t_port;
this.utilisateur=t_utilisateur;
this.password=t_password;
this.sipuser=t_sipuser;
this.scriptmontee=t_scriptmontee;
}

//constructeur a partir du fichier XML
public manager_access(XmlTextReader FileConf)
{
FileConf.WhitespaceHandling=WhitespaceHandling.None;

//parsing du fichier de conf et ecriture dans les variables de la classe
while(FileConf.Read())
{

if(FileConf.LocalName=="serveur")
{
this.IPadress = FileConf.ReadString();
FileConf.Read();
this.port = Convert.ToInt32(FileConf.ReadString());
FileConf.Read();
this.utilisateur = FileConf.ReadString();
FileConf.Read();
this.password = FileConf.ReadString();
FileConf.Read();
this.scriptmontee = FileConf.ReadString();
FileConf.Read();
this.sipuser = FileConf.ReadString();
}
}
FileConf.Close();

//recuperation de l'user xlite de la machine
//this.sipuser = Recupere_User_Xlite();
}

~manager_access()
{
MyAstSocket.Close();
}

private string[] SplitByString(string testString, string split) {
int offset = 0;
int index = 0;
int[] offsets = new int[testString.Length + 1];

while(index < testString.Length) {
int indexOf = testString.IndexOf(split, index);
if ( indexOf != -1 ) {
offsets[offset++] = indexOf;
index = (indexOf + split.Length);
} else {
index = testString.Length;
}
}

string[] final = new string[offset+1];
if (offset == 0 ) {
final[0] = testString;
} else {
offset--;
final[0] = testString.Substring(0, offsets[0]);
for(int i = 0; i < offset; i++) {
final[i + 1] = testString.Substring(offsets[i] + split.Length, offsets[i+1] - offsets[i] - split.Length);
}
final[offset + 1] = testString.Substring(offsets[offset] + split.Length);
}
return final;
}

private string Recupere_User_Xlite()
{
//recupere dans la base de registre le nom du user X-Lite en cours
RegistryKey rk = Registry.CurrentUser;
RegistryKey rk2 = rk.OpenSubKey("Software\\CounterPathSolutionsInc\\X-Lite");
return rk2.GetValue("general:Username").ToString();
}
//Fonction de connexion au socket pour dialoguer avec le manager Asterisk
public void Ast_Connecte()
{
this.MyAstSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.MyAstServerEndPoint = new IPEndPoint(IPAddress.Parse(this.IPadress), this.port);
MyAstSocket.Connect(this.MyAstServerEndPoint);

}

//Cette fonction se logue au manager Asterisk et attend un evenement Link
// Si link cela veut dire qu'une communication est etablie pour le user
// on recupere alors le numero et l'on ouvre le browser sur la fiche correspondante
public void Ast_Logue_Et_Ecoute()
{
// Se logue
this.MyAstSocket.Send(Encoding.ASCII.GetBytes("Action: Login\r\nUsername: "+this.utilisateur+"\r\nSecret: "+this.password+"\r\nActionID: 1\r\n\r\n"));
int bytesRead = 0;
char[] delimiterChars = { ' ', '\n' };
string delimiterChars2 = "\r\n\r\n";
do
{
int affiche = 0;
byte[] buffer = new byte[1024];
bytesRead = this.MyAstSocket.Receive(buffer);
string response = Encoding.ASCII.GetString(buffer, 0, bytesRead);

string[] blocs = SplitByString(response,delimiterChars2);
//string[] blocs = response.Split(delimiterChars2);
foreach(string bloc in blocs)
{
string[] reponseclatees = bloc.Split(delimiterChars);

if(Regex.Match(bloc, sipuser, RegexOptions.IgnoreCase).Success
&& Regex.Match(bloc, "Link", RegexOptions.IgnoreCase).Success
&& !Regex.Match(bloc, "UnLink", RegexOptions.IgnoreCase).Success
&& !(Regex.Match(bloc, "Channel2: SIP", RegexOptions.IgnoreCase).Success
&& Regex.Match(bloc, "Channel1: SIP", RegexOptions.IgnoreCase).Success
)
)
{
foreach(string s in reponseclatees)
{
if(affiche == 1)
{
//Si appel entrants lance le browser
if(!Regex.Match(s, sipuser, RegexOptions.IgnoreCase).Success)
{
affiche = 0;
//Lancement du browser et de la fiche
System.Diagnostics.Process.Start(scriptmontee+s);
affiche = 0;
// reponseclatees = null;
// break;
}
//appel sortant ne lance pas le browser
else
{
affiche = 0;
}
}
if(Regex.Match(s, "CallerID1:", RegexOptions.IgnoreCase).Success)
//if(Regex.Match(s, "CallerID2:", RegexOptions.IgnoreCase).Success)
//if(Regex.Match(s, "Channel2:", RegexOptions.IgnoreCase).Success)
{
affiche = 1;
}
else
{
affiche = 0;
}
}
}

if(Regex.Match(bloc, "Message: Authentication accepted", RegexOptions.IgnoreCase).Success)
{
// Send a ping request the asterisk server will send back a pong response.
this.MyAstSocket.Send(Encoding.ASCII.GetBytes("Action: Events\r\nEventMask: ON\r\n\r\n"));
}
}
}while(bytesRead != 0);
}

//accesseurs
public string a_serveur
{
get
{
return this.IPadress;
}
set
{
this.IPadress = value;
}
}
public int a_port
{
get
{
return this.port;
}
set
{
this.port = value;
}
}

public string a_utilisateur
{
get
{
return this.utilisateur;
}
set
{
this.utilisateur = value;
}
}

public string a_password
{
get
{
return this.password;
}
set
{
this.password = value;
}
}

public string a_sipuser
{
get
{
return this.sipuser;
}
set
{
this.sipuser = value;
}
}

public string a_scriptmontee
{
get
{
return this.scriptmontee;
}
set
{
this.scriptmontee = value;
}
}
//Fin accesseurs

}//Fin Classe manager_access



public partial class MainForm
{
[STAThread]
public static void Main(string[] args)
{
Application.Run(new MainForm());
}

public MainForm()
{
this.Activate();

string configuration_file = "C:\\Program Files\\montee_fiche\\config.xml";

//reads config file
XmlTextReader filexml=new XmlTextReader(configuration_file);

// creates an instance configured with the config file

 manager_access MyManager = new manager_access(filexml);

// connects to manager
MyManager.Ast_Connecte();

// Listen to the manager events
MyManager.Ast_Logue_Et_Ecoute();


}
}
}



2013-04-16

PHP script using the Asterisk manager to place a call between two parties

In brief, what is the manager ?

One of the ways to use Asterisk is opening a socket with its manager (by default running on port TCP/5038, can be configured using manager.conf).

When the socket is opened, it is possible to authenticate (Action:Login) and to send commands that Asterisk will treat (see here the full API), it allows also to follow the live logs of Asterisk activity, can be very useful please see here a possible application.

A good exercise is to connect to the manager using telnet and just follow the activity.

What I want to present here is a simple PHP script openning a socket with the manager and sending commands to establish a call between two parties;

The Code

(this page is called from a form that supplies the two extensions to connect, the code is simplified to ease the comprehension);



if($_POST['lancer'])
{
$called=$_POST['extension'];
$caller=$_POST['sipuser'];
echo "$caller you will be connected to ".$called;

// connexion to manager opening a socket ans authenticating wiant an user defined in mamanger.conf
$socket = fsockopen("xxxxxx","5038", $errno, $errstr);
fputs($socket, "Action: Login\r\n");
fputs($socket, "UserName: superuser\r\n");
fputs($socket, "Secret: xxxxxx\r\n\r\n");
fgets($socket);

// Call launch
fputs($socket, "Action: Originate\r\n");
fputs($socket, "Exten: $called\r\n");
fputs($socket, "Context: yyyyyyy\r\n");
fputs($socket, "CallerID: $called\r\n");
fputs($socket, "Priority: 1\r\n");
fputs($socket, "Channel: SIP/$caller\r\n\r\n");

//Disconnection
fputs($socket, "Action: Logoff\r\n");
}

2013-04-15

Meet A2Billing

Context

As an happy administrator of ten Asterisk servers, I was asked to manage a project of prepaid accounts based on our favorite IPBX.

 
The configuration is simple a priori, the client receives the server's phone number, an access code (or recognition of his calling number) and can compose and communicate within his credit limit.

Silly loss of time

I firstly thought that it must be feasible "in house" with a script that will check in a database to identify the caller (either by code or by typed CID) and run the command Dial will if it is authenticated (with a maximum time limit corresponding to a calculation based on the cost of the call and its remaining credit). once the call is finished a small decrement its remaining credit, "c'est tout".


In fact I have spent two days and it was not so trivial, I even got headache with lots of little details.

Eureka

I then decided to search for a tool doing that (I know I did things in reverse, my pride is misplaced :-) ) frankly not convinced to find such a thing as free.

 
I begin to dig in voip-info wiki and try different things more or less successful, more or less free, more or less uninstallable.


And I fell on A2biling AMAZING product PHP / MySQL based that manages not only everything I want but even ten times more (prepaid, postpaid, identification by CID, recharge cards .. .)!


After 3 back flipsand quarter of an hour installed smoothly (frankly awesome http://wiki.asterisk2billing.org/index.php/Installation_guide the wiki, I did follow the the letter) is directly operational tool and is frankly madness in terms of ergonomics and ease of starting to use.

  • You create your users who are authenticated either by their CID either by typing a code.
  • You create your prepaid card, everything is customizable.
  • You create your offers
  • You retrieves stats, invoices ...
  • In short, a multitude of features (which I am far from having made ​​the tour so it is large)

You understand I'm a fan of a2billing and I strongly encourage you to test a

http://www.asterisk2billing.org/cgi-bin/trac.cgi
 
 

Command Asterisk using ".call" files

Apart dialplan or manager, one of the ways to use Asterisk is using ".call".

Asterisk is continuously scanning folder /var/spool/asterisk/outgoing for "call" files, these call files if they are correctly formated will cause Asterisk to launch hte commands they contains.

We can easily imagine how this feature can be useful for repetitive or massing operations...

Send a SMS to yyyyyyyyy using SMS gateway (14974800), requires a context [smsdial] (see extensions.conf)
            Channel: ZAP/g1/14974800
MaxRetries: 1
RetryTime: 60
WaitTime: 30
Context: smsdial
Extension: yyyyyyyyy
Priority: 1
SetVar: MSG=Test SMS coucou
N.B :it is possible to uses smsq in command line to send a SMS ;
smsq --motx-channel= numero_du_centre sur le channel numero_du_destinanataire "test"

Channel:ZAP/g1/XXXXXX
MaxRetries: 1
WaitTime: 20
Application:txfax
Data:/var/spool/asterisk/fax/1179405466.6.tif|caller

In this post, I explain how to combine this feature to simple scripting abilities in order to launch call campaigns easily.

What are the differences between Zaptel and DAHDI ?


Context

It's been a while since I've posted here but I wanted to share my amazing encounter with DAHDI.

Unfortunately being forced to reinstall Asterisk, even more unfortunately the pass was version 1.4.

When picking sources to compile, surprise, our good old zaptel no longer exists, it is replaced by a strange thing called DAHDI.

This kind of surprises are generally not unpleasant apart when you stop by 50 users of prod on the back, not short fun.

After some research it appears that changes with zaptel are not so huge as it seems;


Differences between zaptel and DAHDI - configuration

Compiling dahdi, libpri, asterisk and asterisk-addons takes place without worry.

the zaptel.conf disappears in favor of, dahdi / system.conf whose syntax is similar to if not the echo-cancellation information is now a configurable channel.

Here is an example configuration for a single card TE110P connected to a T2;

span=1,1,0,ccs,hdb3,crc4
bchan=1-15,17-31
dchan=16
echocanceller=mg2,1-15,17-31
# Global data
loadzone = fr
defaultzone = fr

zapata.conf disappears in favor of, dahdi-channels.conf in which I have made no changes.

In extensions.conf classic Dial (ZAP/g1 ... is simply replaced by Dial (DAHDI/g1 ... (a simple string replacement is enough).


Differences between zaptel and DAHDI - tools

Regarding the various existing tools for configuration and monitoring of channels and interfaces; the good old ztcfg becomes dahdi_cfg, zttest becomes dahdi_test, zttool becomes dahdi_tool.A lot of other utilities are also available with dahdi (type dahdi + TAB on your linux box to see the list).

To summarize

In summary, there is really no need to panic with dahdi
It seems completely different, but with a minimum of organization, the migration from zaptel to dahdi is very easy.

good luck!


How to recognize in a single SQL query the area code of a phone number


Put a table of codes:

Area| Code
-------------------
Area0 | 2
Area1 | 21
Area2 | 212
Area3 | 213
Area4 | 3
Area5 | 321

A calls table

CallId | CallerId
-------------------------------
1 | 276543
2 | 214525
3 | 213987
4 | 365145
5 | 321458

Is it possible in a single query to find the area corresponding to the code? The difficulty here is obviously that the code has no fixed length, and its length is not limited.

CallId | CallerId | Area
---------------------------------------
1 | 276543 | Area0
2 | 214525 | Area1
3 | 213987 | Area3
4 | 365145 | Area4
5 | 321458 | Area5


The solution is as follow!
The trick is a subquery that selects all areas that can match a code but keeps only the longest, so the most appropriate!

SELECT CallId, CallerId,
(
SELECT Area
FROM codes
WHERE code = LEFT (CallerId, length (code))
ORDER BY length (code) DESC
LIMIT 1
)
FROM Calls


Hoping that it can be useful to someone, if someone knows a better way, however, I'm interested.