czwartek, 19 lutego 2015

Upgrade oprogramowania w SIM900

Jakiś czas temu zakupiłem na ebay moduł SIM900A MINI V4.0 do komunikacji GPRS/GSM.


Po rozpakowaniu zabrałem się za podłączenie modułu w celu przetestowania podstawowych funkcjonalności. Niestety moduł zwracał odpowiedź "PH-NET PIN" - który oznacza, że moduł jest zablokowany dla aktualnej sieci.
Okazało się, że moduły SIM900A nie bez przyczyny był tańsze o jakieś 10$ od modułów SIM900. Moduł SIM900A jest przeznaczony na rynek azjatycki, moduł SIM900 zaś na rynek europejski.
W poszukiwaniu sposobu obejścia tego problemu natrafiłem na identyczny problem: http://amichalec.net/2014/08/sim900a-fixed-for-europe/

Rozwiązanie problemu jest bardzo proste i polega na wgraniu do układu SIM900A oprogramowania przeznaczonego dla układu SIM900.

W celu sprawdzenia oprogramowania należy wywołać komendę AT+CGMR. Wersja oprogramowania znajdująca się w moim układzie to: 1137B03SIM900A64_ST_ENHANCE. Potrzebowałem jedynie wgrać wersję oprogramowania: 1137B02SIM900M64_ST_ENHANCE.

Narzędzia oraz oprogramowania do układu SIM900 znalazłem na blogu: http://dostmuhammad.com/blog/sim900-firmware-update-tutorials-appnotes/

Na wypadek gdyby link powyżej był nieaktualny zamieszczam własne linki do flashera oraz do oprogramowania.

Krótki opis sekwencji wgrywania oprogramowania do SIM900:
  1. Podłączenie do zasilania modułu SIM900A
  2. Skonfigurowanie interfejsu RS232 do pracy z szybkością nie większą niż 19200bps (większa prędkość może spowodować problemy).
  3. Wybranie pliku do wgrania
  4. Zrestartowanie modułu SIM900A.
  5. Po wgraniu oprogramowania pojawi się komunikat o "Download complete" należy wtedy zrestartować modem.

wtorek, 16 marca 2010

W jaki sposób wyświetlić rekordy nie należące do zasobu przy pomocy LINQ?

Niby bardzo proste, ale czasami mogą pojawić się kłopoty. Załóżmy, że mamy dwie tabele Companies, Users oraz CompanyUsers. Ta ostatnia zawiera informację o tym czy użytkownik został przydzielony do firmy. Potrzebujemy wydajnego zapytania, które wyłuska użytkowników, którzy nie zostali przydzieleni do firmy X. Pisząc proste zapytanie SQL wyglądałoby to następująco:

DECLARE @companyID int;
SET @companyID = 2502

SELECT u.UserID, u.[Login]
FROM dbo.FRM_Users as u
LEFT OUTER JOIN Issue.CompanyUsers AS cu ON u.UserID = cu.UserID AND cu.CompanyID = @companyID
WHERE cu.CompanyID IS NULL
Przy wykorzystaniu LINQPad możemy szybko prze konwertować do postaci Linq:
var query = from u in Users
join uc in CompanyUsers.Where(cc => cc.CompanyID == 2502) on u.UserID equals uc.UserID into g
from c2 in g.DefaultIfEmpty()
where c2.CompanyID == null
select new
{
UserID = u.UserID,
UserName = u.Login
};
query.Dump();
Wygenerowany kod SQL będzie następujący:
-- Region Parameters
DECLARE @p0 Int SET @p0 = 2502
-- EndRegion
SELECT [t0].[UserID], [t0].[Login] AS [UserName]
FROM [FRM_Users] AS [t0]
LEFT OUTER JOIN [Issue].[CompanyUsers] AS [t1] ON ([t0].[UserID] = [t1].[UserID]) AND ([t1].[CompanyID] = @p0)
WHERE (([t1].[CompanyID]) IS NULL)
Czyli dokładnie taki jaki chcieliśmy.

wtorek, 2 marca 2010

W jaki sposób wyświetlić rekordy zawierające wartości o dokładności większej niż 2 miejsca po przecinku

Ostatnio napotkałem problem, który powodował, że niektóre rekordy nie wyszukiwały się względem określonej wartości. Powodem okazała się zbyt duża liczba miejsc po przecinku wartości przechowywanej w kolumnie typu money.
Nasuwa się tutaj pytanie:
W jaki sposób szybko wyszukać rekordy w bazie danych, które posiadają wartość kwoty z dokładnością większą niż dwa miejsca po przecinku?
Rozwiązanie było dosyć proste:

SELECT Id, PaymentValue
FROM Payments
WHERE ((PaymentValue * 100) % 1) > 0

czwartek, 8 października 2009

Uwierzytelnianie przy pomocy HttpWebRequest

W tym artykule przedstawię sposobów w jaki można, z poziomu kodu C#, pobrać dane z serwisu wymagającego uwierzytelnienia użytkownika.

Ofiarą naszych testów będzie demo projektu BugTracker.NET służącego do śledzenia błędów. Do uwierzytelnienia wykorzystałem istniejącego użytkownika: 'aa' z hasłem: 'a' (Nie wiem kto go stworzył, ale na szczęście nie starał się wymyślić trudniejszego hasła). W przedstawionym przykładzie spróbujemy wyświetlić stronę edit_bug.aspx, dostępną dla zalogowanych użytkowników.

Co siedzi w nagłówku HTTP?

Zanim jednak zaczniemy pisać kod przyjrzyjmy się nagłówkom wysyłanym z przeglądarki. Możemy wykorzystać do tego dodatek Live HTTP Headers do FireFox umożliwiający podejrzenie nagłówków wysyłanych z naszej przeglądarki.

Przed zalogowaniem usunąć ciastko, które zostało automatycznie wygenerowane podczas pierwszego wejścia na stronę. Żeby usunąć ciastko w FireFox należy w Narzędzia->Opcje->Prywatność ustawić program FireFox 3.5 tak aby pamiętał historię przeglądania. Pojawią się wtedy dwie opcje: wyczyść ostatnią historię oraz usuń pojedyncze ciasteczko.


Wybieramy drugą opcje i powinno pojawić się okienko, w którym już bez problemu znajdziemy interesujące nas ciastko.


Po usunięciu wszystkich ciastek z witryny ifdefined.com odpalamy nasz nowo zainstalowany dodatek a następnie wchodzimy na stronę http://ifdefined.com/btnet/default.aspx. Nagłówki HTTP wysłane do przeglądarki są następujące:

http://ifdefined.com/btnet/default.aspx?msg=logged+off
POST /btnet/default.aspx?msg=logged+off HTTP/1.1
Host: ifdefined.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: pl,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://ifdefined.com/btnet/default.aspx?msg=logged+off
Content-Type: application/x-www-form-urlencoded
Content-Length: 266
__VIEWSTATE=%2FwEPDwULLTE5MDkwNTkxNzEPZBYEZg8WAh4JaW5uZXJodG1sBRZCdWdUcmFja2VyLk
5FVCAtIGxvZ29uZAIBD2QWAgICDxYCHwBlZGQ82ZXfhLpaFgGMFOOMtJpj97mEtA%3D%3D&
__EVENTVALIDATION=%2FwEWBALs5cVUAtyCl4wHAtDvzu8MAqLAiY0Lu%2FB2bEdR9NFA8%2BTVEy9rLyZz5So%3D
&user=aa&pw=a&ctl01=Logon
HTTP/1.x 302 Found
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Expires: -1
Location: /btnet/bugs.aspx
Server: Microsoft-IIS/7.0
Set-Cookie: ASP.NET_SessionId=1pe5ym2dwe1jg145psjx4e45; path=/; HttpOnly
Set-Cookie: se_id=8587ab9e-0087-4f6a-ae38-1e5eec8741d0; path=/btnet
Set-Cookie: user=name=aa&NTLM=0; expires=Mon, 11-Oct-2010 23:46:06 GMT; path=/btnet
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Sun, 11 Oct 2009 23:46:05 GMT
Content-Length: 137

Teraz gdy już wiemy co załadować do naszego nagłówka, możemy stworzyć sobie metodę:

public string GetBugName(int bugId)
{
string str = "__EVENTVALIDATION=&user=aa&pw=a&ctl01=Logon";
// kontener do przechowywania ciastek
CookieContainer cookies = new CookieContainer();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://ifdefined.com/btnet/default.aspx");
request.KeepAlive = true;
request.Method = WebRequestMethods.Http.Post;
request.ContentType = "application/x-www-form-urlencoded";
// str.Lenght zwróci jedynie liczbę znaków w ciągu,
// w przypadku kodowania UTF-8 należy zastosować tutaj
// funkcję GetByteCount zwracającą liczbę bajtów ciągu string.
request.ContentLength = Encoding.UTF8.GetByteCount(str);
request.CookieContainer = cookies;
request.Referer = "http://ifdefined.com/btnet/bugs.aspx";

byte[] contentUTFbytes = Encoding.UTF8.GetBytes(str);
using(BinaryWriter bnOut = new BinaryWriter(request.GetRequestStream(), Encoding.UTF8))
{
bnOut.Write(contentUTFbytes);
bnOut.Flush();
}

// Po wywołaniu metody GetRequest() do zmiennej cookies zostają załadowane ciastka
request.GetResponse();
request = (HttpWebRequest)WebRequest.Create("http://ifdefined.com/btnet/edit_bug.aspx?id=" + bugId);
request.Method = WebRequestMethods.Http.Get;
// przekazujemy wczytane ciastka do kontenera
request.CookieContainer = cookies;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
string data = sr.ReadToEnd();
// (?:.*?) - pomiń wszystkie znaki pomiędzy
// (?.*?) - wczytaj zawartość do zmiennej content
string pattern = "<input name=\"short_desc\"(?:.*?)value=\"(?.*?)\" />";
if(Regex.IsMatch(data, pattern, RegexOptions.IgnoreCase))
{
Match math = Regex.Match(data, pattern, RegexOptions.IgnoreCase);
return math.Groups["content"].Value;
}
return string.Empty;
}
}

W ten oto prosty sposób pobraliśmy dane z serwisu wymagającego uwierzytelnienia użytkownika przy pomocy kodu C#.

Pozdrawiam,
Cezary Suchowolec

wtorek, 6 października 2009

Backup bazy przy pomocy narzędzia osql

Ostatnio zastanawiałem się w jaki sposób usprawnić automatyczne tworzenie Backup-ów na MS SQL Server. Bardzo pomocne okazało się narzędzie osql działające w trybie tekstowym. Wszystkie niezbędne informację o sposobie wywołania oraz parametrach wejściowych możecie znaleźć w zamieszczonym odnośniku.

Jak to działa?

Zacznijmy od prostego skryptu do tworzenia backup-u określonej bazy.

@echo off
osql -s localhost -E -d BazaDB -Q "BACKUP DATABASE BazaDB TO DISK = 'D:\Backup\BazaDB.bak'"
Po uruchomieniu skryptu powinniśmy otrzymać backup bazy we wskazanej lokalizacji.

Ciągłe edytowanie plików Batch mija się z celem, potrzebujemy rozwiązanie uniwersalne, które podczas uruchamiania nie będzie nadpisywało wcześniej utworzonych plików. W tym celu stworzymy skrypt, który wygeneruje nazwy plików oraz wypełni parametry wywołania metody. Wykorzystamy do tego celu komendy date i time.

@echo off
@cls
@echo *******************************************************************************
@echo * Backup database *
@echo *******************************************************************************
@echo.

@set Dir=D:\Backup\BazaDB\
@set FileName=BazaDB
@set DbName=BazaDB
@set Now=%Time: =0%
@set DateTime=%date:~0,4%%date:~5,2%%date:~8,2%_%Now:~0,2%%Now:~3,2%%Now:~6,2%

@set BackupFile=%FileName%_%DateTime%.bak
@set BackupPath=%Dir%%BackupFile%

@echo Nazwa pliku: %BackupFile%
@echo Lokalizacja: %BackupPath%
Po uruchomieniu skryptu otrzymamy otrzymamy:

*******************************************************************************
* Backup database *
*******************************************************************************

Nazwa pliku: BazaDB_20091006_223618.bak
Lokalizacja: D:\Backup\BazaDB\BazaDB_20091006_223618.bak

C:\Documents and Settings\czarek>
Fajnie, udało się, lecz to nie koniec, musimy zabezpieczyć się przed zapchaniem dysku. W tym celu wykorzystamy darmowe narzędzie 7zip. Do kompresji plików skorzystamy z parametrów:

  1. a - dodawanie plików do archiwum

  2. -t - określa typ archiwum

  3. -p - hasło do archiwum

Do naszego skryptu dodamy dwie zmienne ZipFile oraz ZipPath, będzie wyglądał on teraz następująco:

@echo off
@cls
@echo *******************************************************************************
@echo * Backup database *
@echo *******************************************************************************
@echo.

@set Dir=D:\Backup\BazaDB\
@set FileName=BazaDB
@set DbName=BazaDB
@set Now=%Time: =0%
@set DateTime=%date:~0,4%%date:~5,2%%date:~8,2%_%Now:~0,2%%Now:~3,2%%Now:~6,2%

@set BackupFile=%FileName%_%DateTime%.bak
@set BackupPath=%Dir%%BackupFile%

@set ZipFile=%FileName%_%DateTime%.7z
@set ZipPath=%Dir%%ZipFile%

osql -s localhost -E -d %DbName% -Q "BACKUP DATABASE %DbName% TO DISK = '%BackupPath%'"

@echo Compression file '%BackupPath%' P
"C:\Program Files\7-Zip\7z.exe" a -t7z %ZipPath% %BackupPath% -pSecretP@ssw0rD

DEL %BackupPath%
Na koniec usuwamy plik backup-u.

W ten sposób utworzyliśmy prosty skrypt do tworzenia backup-u pojedynczej bazy. Teraz trochę zabawy w Schedulerze i gotowe. Miłej zabawy.

Pozdrawiam,
Cezary Suchowolec