Friday, 20 March 2009
GSM Net Monitor verze 0.10.0
Homepage aplikace.
Instalační cab.
Návod na rozchození lokalizace pozice pomocí BTS
Změny ve verzi 0.10.0.
- Odstraněna chyba, která se mohla projevit tím, že se v detailech o cell id, které se načítají z csv souborů, nemusely některé informace zobrazit, i když v csv souboru informace byla. Děkuji uživateli "Santana” za report chyby.
- Interní změny v implementaci “observera” RIL vrstvy.
Důležité:
Před instalací nové verze vypněte v aplikaci sledování sítě. Nejlépe starou verzi také sami deaktivujte v nastavení Today obrazovky a odnistalujte ji přes applet Přidat-Odebrat programy.
Protože se jedná o AlFA preview, doporučuji před instalaci Net Monitoru mít v zařízení např. SPB Pocket Plus a v něm aktivovaný safe boot - jestliže by vám "vytuhlo" zařízení, nebudete muset dělat HR (Hard Reset), protože můžete při startu zařízení dočasně deaktivovat Today pluginy.
Program pro jistotu ani nezkoušejte na zařízení, kde je nahráno TouchFlo, nebo jiný agresivní Today plugin. Riskujete zatuhnutí zařízení a je zbytečné mi potom psát dojemné maily, pokud nejste schopni si předtím udělat zálohu PDA nebo alespoň mít funkční safe-boot.
Po upgradu budete muset pravděpodobně znovu zadat svůj registrovaný email a přístupový kód. Pokud jste jej zapomněli, jděte na stránku http://gsmadmin.renestein.net a zadejte znovu svůj email. Aplikace vám nabídne opětovné zaslání emailu s přístupovým kódem.
Jestliže máte zařízení s VGA displejem, v pluginu jsou malé ikony a plugin je vykreslován na malé ploše. Plugin může být vykreslen na větší ploše - přes kontexové menu zobrazte Nastavení pluginu a změňte na záložce Základní nastavení preferovanou výšku na obrazovce Dnes. Ikony ale stejně zůstanou malé, proto chystám plnohotnotnou VGA verzi, do té doby lze plugin plně ovládat přes kontextové menu.
Friday, 20 March 2009 11:16:56 (Central Europe Standard Time, UTC+01:00)
Tuesday, 03 March 2009
LINQ a logování na příkladu logování kroků Dijsktrova algoritmu
Na LINQu je pěkné, jak jednoduše můžeme LINQ výraz upravit nebo jej bezbolestně rozšířit o další části. Nedávno jsem publikoval článek Dijsktrův alogritmus pomocí LINQu, extenzních metod a lambda výrazů a nyní si ukážeme drobnou úpravu v kódu, která způsobí, že se před každým rekurzivním voláním vždy vypíšou i prozatímní výsledky hledání nejkratší cesty.
Abychom mohli zalogovat výsledek, vytvoříme si vlastní extenzní metody pro výpis informací z předaného libovolného generického IEnumerable<T> do konzole.
static class MiscExtensions
{
public static IEnumerable<T> LogToConsole<T>(this IEnumerable<T> source, Func<T, String> logDataSelector, string beginString, string endString)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if(logDataSelector == null)
{
throw new ArgumentNullException("logDataSelector");
}
return innerLogToConsole(source, logDataSelector, beginString, endString);
}
public static IEnumerable<T> LogToConsole<T>(this IEnumerable<T> source, Func<T, String> logDataSelector)
{
return LogToConsole(source, logDataSelector, null, null);
}
public static IEnumerable<T>LogToConsole<T>(this IEnumerable<T> source)
{
return LogToConsole(source, (obj => obj.ToString()), null, null);
}
public static IEnumerable<T> LogToConsole<T>(this IEnumerable<T> source, string beginString, string endString)
{
return LogToConsole(source, (obj => obj.ToString()), beginString, endString);
}
private static IEnumerable<T> innerLogToConsole<T>(IEnumerable<T> source, Func<T, String> selector, string beginString, string endString)
{
if (beginString != null)
{
Console.WriteLine(beginString);
}
foreach (var obj in source)
{
String val = selector(obj);
Console.WriteLine(val);
yield return obj;
}
if (endString != null)
{
Console.WriteLine(endString);
}
}
}
Metod pro logování máme více, abychom nemuseli pokaždé předat všechny argumenty. Prvním argumentem je vždy zdrojová sekvence, o jejíchž prvcích budou logovány informace. Argument logDataSelector nese odkaz na funkci, která umí z objektu ve zdrojové sekvenci získat jeho textovou reprezentaci. Jestliže delegát logDataSelector není předán, je k získání textové reprezentace objektu použita metoda ToString() zdrojového objektu. Další nepovinné argumenty beginString a endString jsou řetězce, které má extenzní funkce zapsat do konzole předtím, než jsou vypsána data o prvním objektu v zdrojové sekvenci (beginString), a po zalogování všech objektů v sekvenci (endString). V našem případě argumenty beginString a endString použijeme k vypsání řetězců, které ohraničí jednolivé kroky algoritmu. Naše extenzní funkce je “neinvazivní”, což znamená, že nefiltruje ani nekonvertuje objekty ve zdrojové sekvenci, ale po vypsání informace o zdrojovém objektu je nezměněný objekt příkazem yield return předán k dalšímu zpracování. Předchozí věta obsahuje varování, že nechcete-li se dočkat nepříjemných překvapení, delegát předaný v argumentu logDataSelector by neměl žádným způsobem měnit data zdrojového objektu, ale pouze je pasivně číst.
Celý algoritmus i s podrobným popisem už zde nebudu opakovat, vložím sem jen pro nás zajímavou rekurzivní metodu getShortestPathInner. Podpora logování je jednoduchou úpravou, protože pouze na námi vybraném neuralgickém místě v LINQ výrazu, které chceme špehovat, zavoláme naši extenzní funkci LogToConsole. Pro lepší orientaci je přidaný kód v následujícím výpisu zvýrazněn tučným červeným písmem.
private static IEnumerable<GraphPath<A0>> getShortestPathInner<A0, A1>(IEnumerable<GraphPath<A0>> initialGraphPath, IEnumerable<A0> processed, IEnumerable<A1> edges)
where A1 : IGraphEdge<A0>
{
var candidates = (from node in edges
where !processed.Contains(node.From)
select node.From).Distinct();
if (candidates.Count() == 0)
{
return initialGraphPath;
}
var minimum = initialGraphPath.Where(gPath => candidates.Contains(gPath.Current)).Min(gPath => gPath.TotalDistance);
var minimumGPath = (from gPath in initialGraphPath
where candidates.Contains(gPath.Current) &&
gPath.TotalDistance == minimum
select gPath).First();
var newGraphPath = from cNode in edges
where cNode.From.Equals(minimumGPath.Current)
select new GraphPath<A0>
{
Current = cNode.To,
Previous = minimumGPath.Current,
TotalDistance = cNode.Distance + minimumGPath.TotalDistance
};
var newGraphResult =
(initialGraphPath.Concat(newGraphPath).Where(obj =>
!initialGraphPath.Any(
obj2 => obj2.Current.Equals(obj.Current) &&
(obj2.TotalDistance < obj.TotalDistance))))
.LogToConsole(obj => String.Format("{0} - {1} - {2}",
obj.Previous, obj.Current, obj.TotalDistance),"--Další kolo algoritmu--", "--Konec kola--")
.ToArray();
var newProcessed = processed.Union(new[] { minimumGPath.Current });
return getShortestPathInner(newGraphResult, newProcessed, edges);
}
}
A zde je ukázka, jak vypadá výstup.
Logovat nemusíte jen do konzole, ale můžete si přidat další extenzní metody, které zohlední vaše speciální nároky, kam a jak se mají informace o objektech v sekvenci logovat. Cílem článku bylo jen ukázat, jak bezbolestné a hlavně elegantní je přidání logování do stávajících LINQ výrazů.
Tuesday, 03 March 2009 16:34:35 (Central Europe Standard Time, UTC+01:00)
.NET Framework | Compact .Net Framework | LINQ
Monday, 02 March 2009
Náhrada ParametrizedThreadStart delegáta v Compact .Net Frameworku
Na fórech o Compact .Net Frameworku (CNF) se často objevují stesky, že v CNF třída Thread nemá konstruktor, který by přijímal delegáta ParametrizedThreadStart. Metodě, na kterou ukazuje delegát ParametrizedThreadStart a která bude spuštěna v novém threadu, můžeme předat jeden argument typu object .
public delegate void ParametrizedThreadStartDelegate(Object obj);
Ty nářky jsou liché, protože můžeme předat do konstruktoru odkaz na instanční metodu bez argumentů ve vlastním objektu, který má ve svých proměnných nebo vlastnostech stavové informace, které použije instanční metoda poté, co je zavolána z metody Start threadu.
Pomocí anonymních metod či lambda výrazů se ale zbavíme nutnosti deklarovat vlastní třídu. Lambda výraz funguje jako adaptér, který převede metodu s jedním argumentem na metodu bez argumentů, kterou očekává konstruktor třídy Thread.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
int myArg = 10;
Thread myThread = new Thread(() => MyThreadMethodWithArgument(myArg));
myThread.Start();
}
void MyThreadMethodWithArgument(Object obj)
{
Console.WriteLine(obj.ToString());
}
}
Jestliže chcete použít syntaxi velmi podobnou použití delegáta ParametrizedThreadStartDelegate ve “velkém” .Net Frameworku, můžete si napsat vlastní třídu ParametrizedThreadStart, která umožňuje konverzi na delegáta ThreadStartDelegate a tedy opět funguje jako adaptér, který můžeme bez problémů předat do konstruktoru třídy Thread.
using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace ParametrizedThread
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
int myArg = 10;
Thread myThread = new Thread(new ParameterizedThreadStart(MyThreadMethodWithArgument, myArg));
myThread.Start();
}
void MyThreadMethodWithArgument(Object obj)
{
Console.WriteLine(obj.ToString());
}
}
public delegate void ParametrizedThreadStartDelegate(Object obj);
public class ParameterizedThreadStart
{
private ParametrizedThreadStartDelegate InnerDelegate { get; set; }
private object Param { get; set; }
public ParameterizedThreadStart (ParametrizedThreadStartDelegate del, object param)
{
InnerDelegate = del;
Param = param;
}
public static implicit operator ThreadStart(ParameterizedThreadStart instance)
{
return (() => instance.InnerDelegate(instance.Param));
}
}
}
Třída ParameterizedThreadStart vyžaduje, abyste do konstruktoru předali argument pro delegáta. Jestliže do konstruktoru argument ihned předat nechcete, ale chcete ve třídě Thread předat argument pro delegáta přetížené metodě Start, tak jako je tomu opět v NF, nezbývá než se na CNF uchýlit k extenzním metodám.
using System;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace ParametrizedThread
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread myThread = new Thread(new ParameterizedThreadStart(MyThreadMethodWithArgument));
myThread.Start();
}
void MyThreadMethodWithArgument(Object obj)
{
Console.WriteLine(obj.ToString());
}
}
public delegate void ParametrizedThreadStartDelegate(Object obj);
public class ParameterizedThreadStart
{
public EventHandler<EventArgs> MyEvent;
private ParametrizedThreadStartDelegate InnerDelegate { get; set; }
private object Param { get; set; }
private bool IsParamSetInConstructor { get; set; }
public ParameterizedThreadStart(ParametrizedThreadStartDelegate del, object param)
{
InnerDelegate = del;
Param = param;
IsParamSetInConstructor = true;
}
public ParameterizedThreadStart(ParametrizedThreadStartDelegate del)
: this(del, null)
{
IsParamSetInConstructor = false;
}
public static implicit operator ThreadStart(ParameterizedThreadStart instance)
{
return (() =>
{
ThreadExtensions.SetThreadData();
var delArg = instance.IsParamSetInConstructor
? instance.Param
: Thread.GetData(Thread.GetNamedDataSlot(ThreadExtensions.THREAD_DATA));
instance.InnerDelegate(delArg);
});
}
}
public static class ThreadExtensions
{
public const string THREAD_DATA = "MethodData";
private static Hashtable _threadDatahashTable = Hashtable.Synchronized(new Hashtable());
public static void Start (this Thread thread, object val)
{
if (thread == null)
{
throw new ArgumentNullException("thread");
}
_threadDatahashTable[thread.ManagedThreadId] = val;
thread.Start();
}
internal static void SetThreadData()
{
object val = null;
val = _threadDatahashTable[Thread.CurrentThread.ManagedThreadId];
_threadDatahashTable.Remove(Thread.CurrentThread.ManagedThreadId);
Thread.SetData(Thread.GetNamedDataSlot(THREAD_DATA), val);
}
}
}
Operátor ThreadStart ve třídě ParametrizedThreadStartDelegate vrací složitější lambda výraz, ve kterém dojde k rozhodnutí, zda bude metodě, na kterou ukazuje InnerDelegate předán argument z konstruktoru, nebo argument, který byl předán extenzní metodě Start. Data specifická pro thread jsou v metodě SetThreadData vyzvednuta z objektu Hashtable a uložena v pojmenovaných datových slotech threadu. Extenzní metoda Start používá pro účely tohoto příkladu objekt Hashtable, protože pro Hashtable je narozdíl od generické třídy Dictionary možné rychle získat její threadově bezpečnou (tedy z větší části threadově bezpečnou ) verzi - Hashtable.Synchronized(new Hashtable()); a náš delegát ParametrizedThreadStartDelegate, přijímající typ object, si stejně na typovou bezpečnost moc nepotrpí. Tyto nevýhody by vás měly přesvědčit, že nejlepší, přímočaré a hacků prosté řešení jsem zmínil na začátku – vytvořte svoji vlastní třídu s instanční metodou a typovými vlastnostmi, které ponesou stavové informace. Další možností může být vytvoření vlastního wrapperu nad nativními API CreateThread a CreateFiber. Chcete-li ale v CNF použít ve třídě Thread delegáta ParametrizedThreadStartDelegate ve stylu NF, znáte nyní více způsobů, jak to provést.
Monday, 02 March 2009 13:48:05 (Central Europe Standard Time, UTC+01:00)
Compact .Net Framework