// Matlab Interface Library
// by Emanuele Ruffaldi 2002
// http://www.sssup.it/~pit/
// mailto:pit@sssup.it
//
// Description: DDE Matlab Library
using System;
using System.Text;
namespace DDEMATLib
{
/// <summary>
/// Specialized Support class for MATLAB DDE
/// - get/set matrix as string or as double [,] vectors
/// - evaluate with/without string result
///
/// </summary>
public class DDEMatlab : DDELibrary.DDEClient, MATLibrary.MATAccess
{
/// <summary>
/// Construct a default DDE connection that uses XLTABLE
/// </summary>
public DDEMatlab() : this(false, true)
{
}
/// <summary>
/// Construct a MATLAB DDE connection letting choose the
/// format and the write mode
/// </summary>
/// <param name="usePOKE"></param>
/// <param name="useXLTABLE"></param>
public DDEMatlab(bool usePOKE, bool useXLTABLE)
{
this.usePOKE = usePOKE;
this.useXLTABLE = useXLTABLE;
Open();
itemEval = CreateStringItem("EngEvalString");
itemEvalResult = CreateStringItem("EngStringResult");
}
/// <summary>
/// Open the connection
/// </summary>
/// <returns></returns>
public bool Open()
{
if(IsOpen) return true;
channel = OpenChannel("Matlab", "Engine");
if(channel == null) return false;
channel.Timeout = 15000;
return true;
}
/// <summary>
/// Gets a matrix variable
/// </summary>
/// <param name="name">name of the matrix</param>
/// <param name="data">the matrix, preallocated or not</param>
/// <returns></returns>
public bool GetMatrix(string name, ref double [,] mtx)
{
if(!IsOpen) return false;
if(!useXLTABLE)
{
string s = GetVariable(name);
if(s == null) return false;
return StringToMatrix(s, ref mtx);
}
else
{
// get variable as XLTable
byte [] x;
DDELibrary.DDEItem di = CreateStringItem(name);
bool r;
r = Channel.RequestXL(di, out x, Channel.Timeout);
DestroyStringItem(di);
if(r && x != null)
{
if(BuggedMATLABXLTable)
return BytesToMatrix(x, ref mtx);
else
return DDELibrary.XLTable.BytesToMatrix(x, ref mtx);
}
else
return false;
}
}
/// <summary>
/// Gets a variable in string format
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public string GetVariable(string name)
{
string r;
if(!IsOpen) return null;
if(Channel.Request(name, out r))
{
return r;
}
else
return null;
}
/// <summary>
/// Puts a variable value using POKE
/// </summary>
/// <param name="name"></param>
/// <param name="data"></param>
/// <returns></returns>
public bool PutVariable(string name, string data)
{
if(!IsOpen) return false;
return Channel.Poke(name, data);
}
/// <summary>
/// Sets a Matrix variable
/// </summary>
/// <param name="name"></param>
/// <param name="data"></param>
/// <returns></returns>
public bool PutMatrix(string name, double [,] mtx)
{
if(!IsOpen) return false;
if(usePOKE)
{
if(useXLTABLE)
{
byte [] q;
bool r;
DDELibrary.DDEItem di = CreateStringItem(name);
DDELibrary.XLTable.MatrixToBytes(mtx, out q);
r = Channel.PokeXL(di, q, Channel.Timeout);
DestroyStringItem(di);
return r;
}
else
return Channel.Poke(name, MatrixToString(mtx));
}
else
return EvaluateNoResult(name+"="+MatrixToString(mtx, true)+";");
}
/// <summary>
/// Evaluates an expression without getting the result text
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public bool EvaluateNoResult(string s)
{
return IsOpen && Channel.Execute(s, itemEval, channel.Timeout);
}
/// <summary>
/// Evaluates an expresion returning a string
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public string EvaluateAsString(string s)
{
if(!IsOpen) return null;
if(Channel.Execute(s, itemEval, channel.Timeout))
{
string r;
if(Channel.Request(itemEvalResult, out r, channel.Timeout))
return r;
else
return null;
}
else
return null;
}
/// <summary>
/// Convert a double[,] matrix into a string for MATLAB textual
/// transfer
/// </summary>
/// <param name="m"></param>
/// <returns></returns>
public static string MatrixToString(double [,] m)
{
return MatrixToString(m, false);
}
/// <summary>
/// Same as MatrixToString but uses prefixes, suffixes and separators
/// </summary>
/// <param name="m"></param>
/// <returns></returns>
public static string MatrixToString(double [,] m, bool asEval)
{
string rowsep = asEval ? ";" : "\r\n";
char colsep = asEval ? ',' : '\t';
StringBuilder sb = new StringBuilder(100);
int n1 = m.GetLength(0);
int n2 = m.GetLength(1);
if(asEval)
sb.Append('[');
for(int i = 0; i < n1; i++)
{
sb.Append(m[i,0]);
for(int j = 1; j < n2; j++)
{
sb.Append(colsep);
sb.Append(m[i,j]);
}
sb.Append(rowsep);
}
if(asEval)
sb.Append(']');
return sb.ToString();
}
/// <summary>
/// Convert a double[] array considering it as a matrix with
/// the specified number of columns
/// </summary>
/// <param name="m"></param>
/// <param name="cols"></param>
/// <returns></returns>
public static string MatrixToString(double [] m, int cols)
{
StringBuilder sb = new StringBuilder(100);
int n1 = cols;
int n2 = m.Length / n1;
int k = 0;
for(int i = 0; i < n2; i++)
{
sb.Append(m[k++]);
for(int j = 1; j < n1; j++)
{
sb.Append('\t');
sb.Append(m[k++]);
}
sb.Append("\r\n");
if(i == 0)
{
sb.EnsureCapacity(sb.Length*n2);
}
}
return sb.ToString();
}
/// <summary>
/// Transforms back a string into a double[,]
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static double [,] StringToMatrix(string s)
{
double [,] p = null;
StringToMatrix(s, ref p);
return p;
}
/// <summary>
/// Transforms back a string into a double[,]
/// </summary>
/// <param name="s"></param>
/// <param name="q"></param>
/// <returns></returns>
public static bool StringToMatrix(string s, ref double[,] q)
{
// first examine find the number of columns
// then the number of rows
int rows = 0;
int cols = 0;
string [] aa;
int idx;
char [] sep = {'\x9'};
// count columns
idx = s.IndexOf('\r');
if(idx == -1) return false;
aa = s.Substring(0,idx).Split(sep);
cols = aa.Length;
if(cols < 1) return false;
// count rows (number of
idx = 0;
rows = 0;
while((idx = s.IndexOf('\r', idx)) != -1)
{
idx++;
rows++;
}
if(q == null || q.GetLength(0) != rows || q.GetLength(1) != cols)
{
q = new double[rows,cols];
}
idx = 0;
for(int i = 0; i < rows; i++)
{
// get a line and split
int newidx = s.IndexOf('\r', idx);
aa = s.Substring(idx, newidx-idx).Split(sep, cols);
if(s[newidx+1] == '\n')
idx = newidx+2;
else
idx = newidx+1;
for(int j = 0; j < cols; j++)
{
q[i,j] = Double.Parse(aa[j]);
}
}
return true;
}
DDELibrary.DDEChannel Channel
{
get { return channel; }
}
/// <summary>
/// Tells if the connection is open
/// </summary>
public bool IsOpen
{
get { return Channel != null; }
}
protected override void Dispose(bool disp)
{
if(Active)
{
DestroyStringItem(itemEval);
DestroyStringItem(itemEvalResult);
itemEval = new DDELibrary.DDEItem();
itemEvalResult = itemEval;
if(channel != null)
{
channel.Disconnect();
channel = null;
}
base.Dispose(disp);
}
}
/// <summary>
/// Say if the MATLAB window is visible
/// </summary>
/// <returns></returns>
public bool IsVisible()
{
// TODO
return true;
}
/// <summary>
/// Evaluate MAT access
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public bool Evaluate(string s)
{
string r = EvaluateAsString(s);
return r != null;
}
/// <summary>
/// Fixes the MATLAB windows visibility
/// </summary>
/// <param name="b"></param>
public void SetVisible(bool b)
{
// TODO
}
/// <summary>
/// Sets a matrix variable
/// </summary>
/// <param name="name"></param>
/// <param name="data"></param>
/// <returns></returns>
public bool SetMatrix(string name, double [,] data)
{
return this.PutMatrix(name, data);
}
/// <summary>
/// Support functions that transform a block of bytes in XLTable
/// format into a double[,] matrix. It's specialized form MATLAB
/// because in R12 it's broken for data blocks greater than 64K
/// </summary>
/// <param name="tab"></param>
/// <param name="mtx"></param>
/// <returns></returns>
public static bool BytesToMatrix(byte [] tab, ref double [,] mtx)
{
DDELibrary.XLTable xt = new DDELibrary.XLTable(tab);
int nr = xt.Rows;
int nc = xt.Columns;
// check data type
if(xt.NextBlock() != DDELibrary.XLTable.Type.Float)
return false;
// check data size
if(tab.Length < nr*nc*8+8+4)
return false;
// allocate matrix
if(mtx == null || xt.Rows != mtx.GetLength(0) || xt.Columns != mtx.GetLength(1))
mtx = new double[xt.Rows, xt.Columns];
System.IO.BinaryReader br = xt.Stream;
// now get the whole array ignoring the block
try
{
for(int i = 0; i < nr; i++)
for(int j = 0; j < nc; j++)
mtx[i,j] = br.ReadDouble();
}
catch(Exception )
{
return false;
}
return true;
}
DDELibrary.DDEChannel channel;
DDELibrary.DDEItem itemEvalResult, itemEval;
bool usePOKE;
bool useXLTABLE;
/// <summary>
/// Option to fix XLTable to Matrix convertion method
/// </summary>
public static bool BuggedMATLABXLTable = true;
}
}