BAD DESIGN czyli zły projekt

, niedziela, 17 kwietnia 2016 0 komentarze

Czym jest tzw. "zły projekt" (BAD DESIGN)
Niestety obecnie w dobie masy frameworków, bibliotek i innych gotowych komponentów korzystając z tych udogodnień zapomina się w ramach projektowania aplikacji czy nawet wielkich systemów o tak podstawowych zagadnieniach jak dobre zaprojektowanie systemu czy aplikacji. Napisałem "niestety", ponieważ złe zaprojektowanie prowadzi nieuchronnie do klęski w postaci:

  • błędów kodu trudno wykrywalnych lub niemożliwych do wykrycia (często rozwiązywanych np. poprzez cykliczne restarty systemów)
  • frustracji ludzi / programistów którzy są zmuszeni do utrzymywania systemu 
  • braku możliwości jakiegokolwiek rozwoju aplikacji czy systemu lub możliwości znacząco utrudnionych
  • ogromnych kosztów wymaganych na rozwój czy poprawę systemu / aplikacji

Wykorzystanie udogodnień jakie w dzisiejszych czasach występują znacząco ułatwia projektowanie systemów jednakże sprawia również, że używając ich następuje tzw. rozleniwienie i oczekiwanie, że zastosowane bibliotek / frameworków zwalnia od myślenia o dobrym projekcie. No dobrze, ale co to jest "złe zaprojektowanie" lub czym się charakteryzuje ? Zadając to pytanie, najprawdopodobniej padnie tyle odpowiedzi ilu ludzi będzie zaangażowanych w debatę i pewnie każdy będzie miał po części rację. Natomiast pytanie brzmi, czy są bezsporne kryteria złej architektury, z którymi są w stanie zgodzić się architekci / inżynierowie, a jeżeli tak to jakie to są kryteria. Niżej przedstawiam propozycję cech, zaczerpniętych z publikacji internetowych, którymi charakteryzuje się zła architektura (myślę, że do zaakceptowania dla każdego inżyniera):

  • system, który jest ciężki do zmiany z uwagi na fakt, że każda wprowadzona zmiana ma wpływ na znaczącą ilość elementów systemu (Rigidity)
  • system, w którym wprowadzenie zmiany w jakimś kawałku kodu powoduje błędy lub uszkodzenia w nieprzewidywalnych innych miejscach systemu (Fragility) 
  • system, w którym komponenty zostały tak zaprojektowane, że są niemożliwe do wyodrębnienia z tego systemu w celu użycia ich w innym systemie (Immobility) 

Oczywiście nie są to jedyne cechy błędnego projektu czy architektury, jednak gdy te w/w występują w projekcie systemu bezspornie można uznać ten projekt za błędy czyli przykład BAD DESIGN.


Enigma2: eServiceCenter - opis i wykorzystanie

, czwartek, 20 września 2012 0 komentarze

W tym artykule umieszczę opis API i wykorzystania dla klasy eServiceCenter.
Klasa eServiceCenter ma zdefiniowaną jedną statyczną funkcję getInstance(), która zwraca obiekt klasy iServiceHandlerPtr.

Obiekt iServiceHandlerPtr daje dostęp do interfejsów umożliwiających określoną obsługę przekazanego jako referencja serwisu:

  • iPlayableServicePtr play(eServiceReference ref)
  • iRecordableServicePtr record(eServiceReference ref)
  • iListableServicePtr list(eServiceReference ref)
  • iStaticServiceInformationPtr info(eServiceReference ref)
  • iServiceOfflineOperationsPtr offlineOperations(eServiceReference ref)


Opis klasy iListableServicePtr, która daje możliwość obsługi serwisu jako listy:
  • [][] getContent(STRING fmt, BOOLEAN sort): pobranie listy podserwisów danego serwisu np. zwraca kanały zgrupowane w bukiet. W przypadku, gdy wybrano zwracanie dokładnie jednej wartości odpowiadającej serwisowi w definicji formatu, każda wartość jest zwracana bezpośrednio jako pozycja listy wynikowej. W przypadku, gdy wybrano zwracanie większej liczby wartości, rezultat składa się z listy tablic (tuple) wartości.
    fmtwymaganyokreślenie formatu i porządku zwracanych danych. Możliwe opcje to:
    RService Reference (as swig object .. this is very slow)
    SService Reference (as python string object .. same as ref.toString())
    CService Reference (as python string object .. same as ref.toCompareString())
    NService Name (as python string object)
    sortopcjonalnyokreśla czy wyniki mają zostać posortowane - domyślnie False
  • eServiceReference getNext(): zwarca informację o następnym serwisie w ramach kursora / iteratora dla listy
  • NUMBER compareLessEqual(eServiceReference ref1, eServiceReference ref2): porównuje ze sobą dwa serwisy i zawraca rezultat w postacji wartości 1 lub 0, gdzie 0 oznacza równość. Priorytet mają katalogi.
  • iMutableServiceList startEdit(): rozpoczyna edycję serwisu katalogowego. Zwraca interfejs umożliwiający akcje edycyjne.


Opis klasy iStaticServiceInformationPtr:
  • STRING getName(eServiceReference ref)
  • NUMBER getLength(eServiceReference ref)
  • eServiceEvent getEvent(eServiceReference ref, NUMBER start_time=-1)
  • BOOLEAN isPlayable(eServiceReference ref, eServiceReference ignore, BOOLEAN simulate=false)
  • NUMBER getInfo(eServiceReference ref, int w)
  • STRING getInfoString(eServiceReference ref, int w): pobierz informację o parametrze serwisu danym jako 'w' jako String. Wartości parametru 'w' zdefiniowane są w klasie iServiceInformation
  • getInfoObject(eServiceReference ref, int w)
  • BOOLEAN setInfo(eServiceReference ref, int w, int v)
  • BOOLEAN setInfoString(eServiceReference ref, int w, STRING v)


Opis klasy iServiceOfflineOperationsPtr:


Opis klasy iRecordableServicePtr:


Opis klasy iPlayableServicePtr:
  • start()
  • stop()
  • setTarget(int target)
  • iSeekableServicePtr seek()
  • iPauseableServicePtr pause()
  • iServiceInformationPtr info()
  • iAudioTrackSelectionPtr audioTracks()
  • iAudioChannelSelectionPtr audioChannel()
  • iSubserviceListPtr subServices()
  • iFrontendInformationPtr frontendInfo()
  • iTimeshiftServicePtr timeshift()
  • iCueSheetPtr cueSheet()
  • iSubtitleOutputPtr subtitle()
  • iAudioDelayPtr audioDelay()
  • iRdsDecoderPtr rdsDecoder()
  • iStreamableServicePtr stream()
  • iStreamedServicePtr streamed()
  • iServiceKeysPtr keys()


Opis klasy iMutableServiceListPtr, która daje możliwość obsługi serwisu jako listy:
  • flushChanges(): zastosowanie wprowadzonych zmian
  • BOOLEAN addService(eServiceReference ref, eServiceReference before): dodanie serwisu do listy przed serwisem 'before'. Funkcja zwraca informację czy powiodło się dodanie serwisu
  • BOOLEAN removeService(eServiceReference ref): usunięcie serwisu z listy. Zwraca czy powiodło się usunięcie
  • BOOLEAN moveService(eServiceReference ref, NUMBER pos): przesuwa serwis na określoną drugim parameterem pozycję listy
  • BOOLEAN setListName(STRING name): ustawienie nazwy dla listy

Enigma2: eEPGCache - opis klasy i wykorzystanie

, 2 komentarze

Artykuł poświęcam opisowi klasy eEPGCache z pakietu Enigma2.

Funkcje i ich znaczenie wraz z API:

1. setCacheFile(String fileName)
Funkcja ustawia wartość ścieżki do pliku wykorzystywanego w ramach załadowania lub zapisania informacji z bazy EPG - patrz: load() i save(). Parametr fileName powinien być typu STRING.

przykład:

eEPGCache.getInstance().setCacheFile('/hdd/epg.dat')


2. save()
Funkcja służy do zapisu zawartości bazy EPG do wskazanego przy pomocy setChacheFile() pliku płaskiego.


3. load()
Funkcja służy do wciągnięcia zawartości pliku płaskiego wskazanego przez setCacheFile() do bazy EPG.


4. startTimeQuery(eServiceReference ref, NUMBER beginTime = -1, NUMBER minutes = -1)
refwymaganyreferencja do serwisu
beginTimeopcjonalnyczas startu dla zdarzeń zwracanych w kursorze; -1 oznacza brak definicji (dowolny)
minutesopcjonalnydługość odcinka czasowego dla zdarzeń zwracanych w kursorze; -1 oznacza brak definicji (dowolny)
Funkcja służy do inicjalizacji kursora przechodzącego po zdarzeniach EPG. Przygotowany przy użyciu tej funkcji kursor zawiera zdarzenia przecinające odcinek czasu zdefiniowany poprzez beginTime i minutes. Jest powiązana z funkcją getNextTimeEntry(), która to funkcja służy do pobrania następnej w kolejności wartości z kursora. Funkcja zwraca wartość -1 w przypadku, gdy kursor jest pusty.

przykład:
if ptr.startTimeQuery(ref) != -1:
    self.session.open(EPGSelection, ref)
else:
    self.session.open(MessageBox, "Sorry no epg currently available for this service.", MessageBox.TYPE_INFO)


5. eServiceEvent getNextTimeEntry()
Funkcja zwraca następną w kolejności wartość z kursora zainicjalizowanego przez startTimeQuery(). Wywołanie funkcji możliwe jest jedynie po wcześniejszym zainicjalizowaniu.


6. eServiceEvent lookupEventTime(eServiceReference ref, NUMBER begin, NUMBER direction)
refwymaganyreferencja do serwisu
beginopcjonalnyokreślenie horyzontu czasowego dla wyszukiwania zdarzenia; -1 oznacza bieżącą datę
directionopcjonalnydefinicja kierunku przeszukiwania - wartości [-1,0,1]

Funkcja przeszukuje EPG w celu znalezienia zdarzenia dla określonego przez ref serwisu. Zwracane w wyniku działania funkcji jest zdarzenie poprzednie (direction=-1), bieżące (direction=0) lub następne (direction=1) dla horyzontu czasowego określonego przez parametr begin. W przypadku ustawienia parametru begin na wartość -1, brana jest bieżąca data jako horyzont czasowy.

przykład:
queryTime=self.begin+(self.end-self.begin)/2
rec_ref = self.session.nav.getCurrentlyPlayingServiceReference()
evt = eEPGCache.getInstance().lookupEventTime(rec_ref, queryTime)

epg = eEPGCache.getInstance()
ref = self.session.nav.getCurrentlyPlayingServiceReference()
ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
event = epg.lookupEventTime(serviceref, -1, 0)

def getEvent(self):
    service = self.navcore.getCurrentService() 
    info = service and service.info() 
    ret = info and info.getEvent(self.now_or_next) 
    if not ret and info:
        refstr = info.getInfoString(iServiceInformation.sServiceref)
        ret = eEPGCache.getInstance().lookupEventTime(eServiceReference(refstr), -1, self.now_or_next and 1 or 0)
    return ret


7. eServiceEvent lookupEventId(eServiceReference ref, NUMBER event_id)
refwymaganyreferencja do serwisu
event_idwymaganyidentyfikator zdarzenia do wyszukania
Funcja służy do wyszukania w podanym serwisie zdarzenia o określonym parametrem identyfikatorze.

przykład:
event = eEPGCache.getInstance().lookupEventId(service.ref, eventid)


8. [][] lookupEvent([STRING fmt, (eServiceReference ref, NUMBER queryType, NUMBER pe1, NUMBER pe2)])
fmtwymaganyFormat zwracanych danych - opis niżej
refwymaganyreferencja do serwisu
queryTypewymaganytyp zadawanego zapytania - możliwe wartości w tabeli poniżej
pe1wymaganyparametr dodatkowy 1 - zależny od kontekstu ustawionego przez queryType - tabela poniżej
pe2opcjonalnyparametr dodatkowy 2 - zależny od kontekstu ustawionego przez queryType - określa koniec zakresu czasowego - tabela poniżej

Tabela możliwych wartości pola formatu:
WARTOŚĆOPIS
0PyLong(0)
IEvent Id (id zdarzenia)
BEvent Begin Time (czas rozpoczęcia zdarzenia)
DEvent Duration (czas trwania zdarzenia)
TEvent Title (tytuł zdarzenia)
SEvent Short Description (krótki opis zdarzenia)
EEvent Extended Description (rozszerzony opis zdarzenia)
CCurrent Time (bieżący czas)
RService Reference (referencja do serwisu)
NService Name (nazwa serwisu)
nShort Service Name (krótka nazwa serwisu)
XFlaga nie związana ze zwracanym rezultatem określająca, że zwracana ma być minimum jedena pozycja w tabeli wynikowej odpowiadająca każdemu serwisowi nawet, gdy nie znaleziono zdarzeń dla serwisu. W przypadku, gdy nie znaleziono zdarzeń w wynikowym wierszy tabeli wartości nieokreślone są zwracane jako None

Opis parametrów związanych z typem zapytania:
TYPOPISPE1PE2
2zapytanie o identyfikator zdarzeniawartość identyfikatora zdarzeniaNiedotyczy
-1zapytanie o zdarzenia przed określonym parametrem pe1 czasemhoryzont czasowy dla wyszukania (-1 - obecna data)data określająca datę końca dla zakresu przeszukiwania
0zapytanie o zdarzenia przecinające określony parametrem pe1 czashoryzont czasowy dla wyszukania (-1 - obecna data)data określająca datę końca dla zakresu przeszukiwania
+1zapytanie o zdarzenia po określonym parametrem pe1 czasiehoryzont czasowy dla wyszukania (-1 - obecna data)data określająca datę końca dla zakresu przeszukiwania

Funkcja służy do wyszukiwania informacji o zdarzeniach dla podanych parametrów. Informacja zwracana jest w postaci tablicy dwuwymiarowej, gdzie pierwszy wymiar definiuje poszczególne elementy kursora (wyszukane zdarzenia), a drugi wymiar to wartości odpowiadające zdarzeniu które są określone polem Format (dane pojedynczego zdarzenia). Pola czasowe typu NUMBER definiowane są jako liczba sekund od daty EPOCH (w systemach unixowych początek roku 1970).

Przykłady:
events = eEPGCache.getInstance().lookupEvent(['IBDTSENC', (ref, 0, begintime, endtime)])

search = ['IBDCTSERNX']
if services: # It's a Bouquet
    search.extend([(service, 0, -1) for service in services])
events = eEPGCache.getInstance().lookupEvent(search)

search = ['IBDCTSERNX']
if services: # It's a Bouquet
    search.extend([(service, 0, -1) for service in services])
events = eEPGCache.getInstance().lookupEvent(search)
print 'The first event duration: %s'%(str(events[0][2]))

event = eEPGCache.getInstance().lookupEvent(['ESX', (ref, 2, int(idev))])


9. [][] search((STRING fmt, NUMBER size, NUMBER queryType, pe1, pe2))
fmtwymaganyFormat zwracanych danych - opis niżej
sizewymaganymaksymalna liczba zwróconych wierszy
queryTypewymaganytyp zadawanego zapytania - możliwe wartości w tabeli poniżej
pe1wymaganyparametr dodatkowy 1 - zależny od kontekstu ustawionego przez queryType - tabela poniżej
pe2opcjonalnyparametr dodatkowy 2 - zależny od kontekstu ustawionego przez queryType


Tabela możliwych wartości pola formatu:
WARTOŚĆOPIS
IEvent Id (id zdarzenia)
BEvent Begin Time (czas rozpoczęcia zdarzenia)
DEvent Duration (czas trwania zdarzenia)
TEvent Title (tytuł zdarzenia)
SEvent Short Description (krótki opis zdarzenia)
EEvent Extended Description (rozszerzony opis zdarzenia)
RService Reference (referencja do serwisu)
NService Name (nazwa serwisu)
nShort Service Name (krótka nazwa serwisu)

Opis parametrów związanych z typem zapytania:
TYPOPISPE1PE2
0wyszukaj zdarzenia podobne (SIMILAR_BROADCASTINGS_SEARCH)referencja do serwisu w postaci stringaidentyfikator zdarzenia
1wyszukaj zdarzenia o określonym dokładnie tytule (EXAKT_TITLE_SEARCH)tekst do wyszukania
  • 0 = wielkość liter ma znaczenia (CASE_CHECK)
  • 1 = wielkość liter nie ma znaczenia (NO_CASECHECK)
2wyszukaj zdarzenia zawierające tekst w tytule (PARTIAL_TITLE_SEARCH)tekst do wyszukania
  • 0 = wielkość liter ma znaczenia (CASE_CHECK)
  • 1 = wielkość liter nie ma znaczenia (NO_CASECHECK)

Funkcja służy do wyszukiwania informacji o zdarzeniach dla podanych parametrów. Informacja zwracana jest w postaci tablicy dwuwymiarowej, gdzie pierwszy wymiar definiuje poszczególne elementy kursora (wyszukane zdarzenia), a drugi wymiar to wartości odpowiadające zdarzeniu które są określone polem Format (dane pojedynczego zdarzenia).

Przykład:
l = eEPGCache.getInstance().search(('RIBND', 1024, eEPGCache.SIMILAR_BROADCASTINGS_SEARCH, refstr, event_id))


W ramach wykorzystania klasy eEPGCache polecam zapoznać się z klasami:

--- Component.ServiceList !!
--- ServiceReference
--- enigma.eServiceEvent
--- Navigation.Navigation
--- enigma.iStaticServiceInformationPtr
--- enigma.iServiceInformationPtr

Enigma2: Opis kodów dla parametru flag w pliku keymap.xml

, piątek, 31 sierpnia 2012 0 komentarze

Poniżej zestawienie znaczeń związanych z parametrem flag dla pojedynczej pozycji z pliku keymap.xml definiującego mapowanie klawiszy pilota na odpowiednie akcje enigmy.

m - MAKE - normalne naciśnięcie klawisza - zdarzenie obsługiwane raz po naciśnięciu
l - LONG - długie naciśnięcie klawisza - zdarzenie obsługiwane raz po 5-ciu powtórkach, powtórki są obsługiwane do zwolnienia
b - BREAK - zwolnienie klawisza - zdarzenie obsługiwane raz po zwolnieniu klawisza
r - REPEAT - powtarzanie włączone / klawisz zablokowany - zdarzenie obsługiwane po m wielokrotnie do czasu zwolnienia klawisza

Dobrą praktyką jest otwieranie okien dialogowych z użyciem flagi b zamiast m.

Enigma2 - tworzenie pakietów OPKG

, poniedziałek, 20 sierpnia 2012 0 komentarze

W tym artykule opiszę proces tworzenia i przeglądania pakietów OPKG, czyli plików z rozszerzeniem IPK. Na początek informacja w jaki sposób rozpakować pakiet. Powiedzmy, że chcemy podejrzeć zawartość pakietu enigma.ipk. W tym celu należy wykonać następujące kroki:

1. Utwórz katalog tymczasowy:

mkdir ./tmp

2. Wejdź do katalogu tmp:

cd tmp

3. Rozpakować zawartość pakietu IPK:

ar -ox ../enigma2.ipk

4. Rozpakować zawartość archiwów:

gunzip ./*.tar.gz

5. Wypakować pliki TAR:

tar xf ./data.tar
tar xf ./control.tar

6. Usunąć pliki TAR:

rm *.tar

Jeżeli chodzi o strukturę pakietu OPKG to jest ona następująca:

package.opk (ar)
+- debian-binary (text)
+- control.tar.gz (tarball)
...+- control (text)
...+- preinst (script)
...+- postinst (script)
...+- prerm  (script)
...+- postrm (script)
+- data.tar.gz (tarball)
...+- system
......+- bin
.........+- opkg-hello (program)

W strukturze jest stały plik tekstowy debian-binary, następnie spakowane skrypty kontrolne control.tar.gz, a następnie spakowane archiwum docelowe, czyli dane data.tar.gz.
Teraz przejdziemy do procesu tworzenia pakietu. W celu utworzenia pakietu wykonujemy:

1. Tworzymy katalog tmp

mkdir ./tmp

2. Wchodzimy do katalogu tmp:

cd tmp

3. Tworzymy plik tekstowy debian-binary z zawartością "2.0"
4. Tworzymy pliki kontrolne i pakujemy je do archiwum control.tar.gz. Do tworzenia archiwum TAR.GZ uzywany komendy:

tar zcvf control.tar.gz LISTA PLIKÓW/KATALOGÓW

5. Pliki danych pakujemy z docelową strukturą katalogową do data.tar.gz
6. Tworzymy pakiet przy użyciu komendy:

ar -r enigma2.ipk LISTA PLIKÓW/KATALOGÓW

Poniżej znajdują się przykładowe zawartości skryptów kontrolnych.

1. Skrypt control:

Package: enigma2-plugin-extensions-sample                                                                             
Version: 1.2-rc1                                                                                                                  
Description: My Sample Plugin                                                                                 
Maintainer: MASL                                                                                      
Section: base                                                                                                                     
Architecture: mipsel                                                                                                              
Priority: optional                                                                                                                
Homepage: xxxxxx                                                                                                                  
Depends: enigma2                                                                                                                  
Source: xxxxx

2. Skrypt postinst:

#!/bin/sh                                                                                                                         
echo ""                                                                                                                           
echo "********************************************************"                                                                   
echo "*  My Plugin                              *"                                                                       
echo "*  Support:  masl@xxx.pl                *"                                                                             
echo "*  Mod by MASL (c) 2012     *"                                                                                  
echo "********************************************************"                                                                   
echo ""                                                                                                                           
echo "Sample Plugin successfully installed! You should restart enigma2 now"                                                          
echo ""                                                                                                                           
exit 0  

2. Skrypt postrm:

#!/bin/sh                                                                                                                                                                                                                                                        rm -rf /usr/lib/enigma2/python/Plugins/Extensions/SamplePlugin                                                              
echo "Plugin removed! You should restart enigma2 now!"                                                                                                                                                                                                        
exit 0


Linux - jak sprawdzić wielkość katalogów w systemie plików

piątek, 10 sierpnia 2012 0 komentarze

W celu wylistowania systemów plików należy wykonać komendę:

df -h

W wyniku dostaniemy listę filesystemów zamontowanych w naszym linuxie, czyli coś w rodzaju:
Filesystem                Size      Used Available Use% Mounted on
ubi0:rootfs             102.1M     99.3M      2.8M  97% /
udev                      2.0M     88.0K      1.9M   4% /dev
/dev/mtdblock2            2.0M    388.0K      1.6M  19% /boot
tmpfs                    67.6M    268.0K     67.3M   0% /var
tmpfs                    67.6M     16.0K     67.6M   0% /tmp
tmpfs                    67.6M         0     67.6M   0% /dev/shm
/dev/sda1               931.2G     92.3G    838.9G  10% /media/hdd

W celu sprawdzenia wielkości katalogów w wybranym filesystemie należy wykonać:
du -shx -d 1 /

Znaczenie opcji:
- s - wyświetlenie jedynie wartości sumarycznej
- h - pokaż raport w formie czytelnej
- x - zawężenie do bezpośredniego filesystemu - katalogi z innych filesystemów są pomijane w końcowym raporcie
- d - głębokość pokazywanej struktury - wartość 1 oznacza pokazanie tylko bezpośrednich "dzieci"

Przykładowy wynik wykonania komendy:
560.0K /bin
4.2M /etc
21.4M /lib
137.0M /usr
132.0K /home
1.6M /sbin
0 /universe
26.9M /media
191.8M /

VU+ DUO Enigma2 - informacja o portach

, poniedziałek, 30 lipca 2012 0 komentarze

Ostatnio zostałem użytkownikiem sprzętu o nazwie VU+ DUO będącego tunerem telewizji satelitarnej opartym na systemie LINUX. Wszystkim tym którzy chcą mieć naprawdę dobrą maszynę z ładnym interfejsem graficznym (tutaj zachęcam do zainstalowania Black Hole) do oglądania telewizji SAT polecam ten sprzęt.

Przestrzegam wszystkich na wstępie przed tworzeniem hasła zawierającego znak AT (@) ponieważ ja takowe miałem i było to źródłem problemów ze steamingiem na iPAD-a i na inne urządzenia również. Pojawiający się błąd na iPAD w ogóle nic nie mówi na temat problemu z URL-em i dlatego ta przestroga - pół dnia na to straciłem żeby stwierdzić gdzie leży przyczyna.

No to teraz wyjaśnienie na temat otwartych portów:

21 - FTP
22 - SSH
23 - Telnet
80 - WebInterface - można zmienić w ustawieniach pluginu (polecam to zrobić - można uniknąć wielu problemów jak zainstaluje się nam inna wtyczka pracująca na porcie 80 i zrestartujemy system)
111 -
139 - Netbios port
445 - port wymiany plików Windows (SMB)
2029 -
8001 - domyślny port streamingu VIDEO
12000 - port nasłuchu serwera CCcam (można wyłączyć nasłuch ustawiając parametr "SERVER LISTEN PORT" na 0 w pliku /etc/CCcam.conf)
16000 - port telnetu dla CCcam
16001 - port usługi WEB dla CCcam: http://ip_CCcam_server:16001
31335 - STREAM-SEC: przeznaczenie to wysyłanie pojedynczego strumienia - wykorzystywany do strumieniowania AUDIO MP2
31337 -
31338 - STREAM-PES: przeznaczenie to strumieniowanie DVB zawierające kilka ES wewnątrz
31339 - STREAM-TS: przeznaczenie to strumieniowanie DVB z wykorzystaniem transport-stream jako kontenera
31340 - UDP-STREAM-PES
31341 - UDP-STREAM-PES
31342 - STREAM-TS-FILE: przeznaczenie to strumieniowanie DVB - odczyt z pliku zamiast z demultipleksera
31343 - STREAM-ES
57076 -
60533 -



SmartGWT: zamknięcie okna po operacji saveData()

, niedziela, 31 lipca 2011 0 komentarze

Przedstawiam rozwiązanie problemu dotyczącego zamknięcia okna po poprawnym wykonaniu operacji save. Problem polega na tym, że po wykonaniu kodu jak poniżej dostaniemy błąd NULL-owej referencji do okna.

public class DynamicFormWindow extends Window {
protected DynamicForm form;
private ActionPanel actionPanel = new ActionPanel();

public DynamicFormWindow(DynamicForm form, String title) {
this.form = form;

setShowShadow(true);
setShadowSoftness(4);
setShadowOffset(3);

this.setTitle(title);
this.setLayoutMargin(4);
this.setShowMinimizeButton(false);
this.setCanDragResize(true);
this.setAutoSize(true);
this.setIsModal(true);
this.setShowModalMask(false);
this.centerInPage();

this.addCloseClickHandler(new CloseClickHandler() {
@Override
public void onCloseClick(CloseClientEvent event) {
destroy();
}
});

VPanel panel = new VPanel();
panel.setPadding(2);
panel.addMember(form);
panel.addMember(this.actionPanel);

this.addItem(panel);

IButton saveButton = new IButton("Zapisz");
saveButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
DynamicFormWindow.this.saveData();
}
});
addActionButton(saveButton);
}

protected void saveData() {
this.form.saveData();
this.destroy();
}

protected void addActionButton(IButton button) {
this.actionPanel.addMember(button);
}

}


Problem polega na tym, że po wykonaniu saveData() automatycznie wykonywane jest destroy(), gdzie saveData() wykonuje akcję asynchronicznie używając GWT-RPC po stronie serwerowej aplikacji.

Rozwiązaniem problemu jest wykorzystanie Callbacka na saveData() oraz funkcjonalności Scheduler-a. W tym celu zmieniamy ciało metody saveData() w następujący sposób:


protected void saveData() {
if (this.form.validate()) {
this.form.saveData(new DSCallback() {
@Override
public void execute(DSResponse response, Object rawData, DSRequest request) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
DynamicFormWindow.this.destroy();
}
});
}
});
}
}


Takie podejście daje nam to, że kod zamykający okno wykonywany jest po tym jak zakończy się operacja saveData() oraz bieżąca pętla obsługi zdarzeń GWT. Oznacza to, że kod zamykający okno wykona się po całkowitym zakończeniu obsługi związanej z akcją saveData().

Dodatkowe metody Schedulera scheduleEntry/scheduleFinally wykonywane są wewnątrz pętli obsługi zdarzeń zaraz przed/po tym jak wykonany zostanie kod związany z obsługą zdarzeń GWT. W przypadku wykonywania manipulacji DOM wewnątrz tej samej pętli obsługi zdarzeń, można przeciwdziałać dodatkowemu odmalowaniu przez browser.

Web Project Template, czyli Eclipse + Maven+ Spring + GWT + Hibernate

, , , , 0 komentarze

W tym artykule chciałbym podzielić się swoim doświadczeniem w ramach konfiguracji projektu typu Web wykorzystującego Spring, SmartGWT, Hiberante. Całość konfiguracji w zamierzeniu ma być budowana przez Maven-a. Zakładam, że do developmentu używane będzie środowisko Eclipse Helios SR 2. Dlatego zacznijmy od konfiguracji środowiska projektowego.

Zaczynamy od ściągnięcia Eclipsa ze strony Eclipse Helios SR2 Packages (v 3.6.2). Teraz, żeby nam się dobrze pracowało instalujemy następujące pluginy:
- Maven Integration for Eclipse Update Site
- Google Update Site for Eclipse 3.6
- GWT Designer Update Site
- SpringSource Update Site for Eclipse 3.6
- SpringSource Update Site for Eclipse 3.6 (Dependencies)
- JBoss Tools Development Milestone Update Site
Po instalacji Eclipse oraz wymienionych pluginów jesteśmy przygotowani do pracy, więc możemy zacząć. Moja propozycja podziału na moduły aplikacji typu Web jest następująca:
- moduł dziedzinowy: zawierający zarówno model dziedzinowy jak i operacje związane z modelem, czyli inaczej mówiąc model JPA oraz związane z nimi DAO (Data Access Objects)
- moduł usług: zawierający bean-y Springowe udostępniające usługi biznesowe w oparciu o model dziedzinowy
- moduł warstwy web: moduł zawierający całość logiki klienckiej, udostępniający warstwę UI dla użytkownika końcowego.

Moduł warstwy web komunikuje się z modułem serwisów w celu wykonywania akcji po stronie serwera. Nie ma on dostępu do modułu domenowego, gdyż moduł serwisów izoluje dostęp do warstwy domenowej.

Zajmijmy się teraz konfiguracją modułów. W tym celu utworzyć należy strukturę plików tak jak zostało to przedstawione na rysunku obok.

Najważniejsze w tym momencie są pliki pom.xml, czyli pliki konfiguracyjne Mavena. Teraz zajmę się ustawieniami w tych plikach. Dobrze, główny plik konfiguracyjny (root) znajdujący się na poziomie katalogów m2-domain, m2-service, m2-web wygląda następująco:

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0
pl.masl.m2
m2-root
1.0.0-SNAPSHOT
pom
M-2 :: ${project.artifactId}


UTF-8
1.6
1.6
4.8.2
3.0.5.RELEASE
3.0.5.RELEASE
3.5.6-Final
1.0.0.Final
2.0.8
5.1.10



m2-domain
m2-service
m2-web




junit
junit
${junit.verion}
test


org.slf4j
jcl-over-slf4j
1.5.8


org.slf4j
slf4j-log4j12
1.5.8





maven-annotation-plugin

http://maven-annotation-plugin.googlecode.com/svn/trunk/mavenrepo






Central Repo
http://repo1.maven.org/maven2


com.springsource.repository.maven.release
http://maven.springframework.org/release/

false



com.springsource.repository.maven.snapshot
http://maven.springframework.org/snapshot/

true



com.springsource.repository.maven.milestone
http://maven.springframework.org/milestone/

false



SmartGWT Repo
http://www.smartclient.com/maven2/


JavaNet
http://download.java.net/maven/2




Co widać w tym pliku to ustawienia dotyczące wersji bibliotek używanych w ramach aplikacji, definicję modułów z których składa się aplikacja oraz definicja repozytoriów dla bibliotek.

Następnie konfigurujemy plik pom.xml dla modułu domenowego:
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0


m2-root
pl.masl.m2
1.0.0-SNAPSHOT


pl.masl.m2
m2-domain
1.0.0-SNAPSHOT
M-2 :: ${project.artifactId}



org.hibernate
hibernate-jpamodelgen
${hibernate-jpa.version}


org.hibernate
hibernate-entitymanager
${hibernate.version}


org.hibernate
hibernate-c3p0
${hibernate.version}


org.hibernate
hibernate-ehcache
${hibernate.version}


org.springframework
spring-test
${spring.version}


org.springframework.data
spring-data-jpa
1.0.0.M2


org.springframework
spring-orm
${spring.version}


mysql
mysql-connector-java
${mysql.connector.version}






org.apache.maven.plugins
maven-compiler-plugin
2.0.2

${maven.compiler.source}
${maven.compiler.target}
${project.build.sourceEncoding}
-proc:none




org.bsc.maven
maven-processor-plugin
1.3.1


process

process

generate-sources

target/metamodel


org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor








org.codehaus.mojo
build-helper-maven-plugin
1.3


add-source
generate-sources

add-source



target/metamodel










Co mamy w tym pliku to zależności od hibernate-a i Springa, definicja pluginu do kompilacji, definicja pluginów do generacji modelu JPA 2.0.

Teraz konfiguracja pliku pom.xml dla modułu usług:
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0

m2-root
pl.masl.m2
1.0.0-SNAPSHOT

pl.masl.m2
m2-service
1.0.0-SNAPSHOT
M-2 :: ${project.artifactId}



pl.masl.m2
m2-domain
1.0.0-SNAPSHOT
jar
compile


aspectj
aspectjrt
1.5.4


aspectj
aspectjweaver
1.5.4


net.sf.dozer
dozer
5.3.2


org.springframework
spring-tx
${spring.version}


org.springframework.security
spring-security-config
${springframework.security.version}






org.apache.maven.plugins
maven-compiler-plugin
2.3.2

${maven.compiler.source}
${maven.compiler.target}
${project.build.sourceEncoding}



org.apache.maven.plugins
maven-source-plugin
2.1.2


attach-sources
verify

jar-no-fork








W tym pliku widać definicję zależności od modułu domenowego, bibliotek Springa oraz pluginów do kompilacji.

No i na koniec definicja pom.xml-a dla modułu webowego:

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">


m2-root
pl.masl.m2
1.0.0-SNAPSHOT


4.0.0
pl.masl.m2
m2-web
war
1.0.0-SNAPSHOT
M-2 :: ${project.artifactId}



2.3.0
2.4
${project.build.directory}/${project.build.finalName}




pl.masl.m2
m2-service
1.0.0-SNAPSHOT


com.google.gwt
gwt-user
${gwtVersion}
provided


com.google.gwt
gwt-servlet
${gwtVersion}
compile


javax.validation
validation-api
1.0.0.GA
test


javax.validation
validation-api
1.0.0.GA
sources
test


com.smartgwt
smartgwt
${smartgwt.version}
compile


com.smartgwt
smartgwt-skins
${smartgwt.version}
compile


com.google.code
gwt-log
3.1.3


net.sf.gwt-widget
gwt-sl
1.3-RC1
jar
compile


gwt-servlet
com.google.gwt


cglib-nodep
cglib




org.springframework
spring-web
${spring.version}


org.springframework
spring-webmvc
${spring.version}



org.springframework.security
spring-security-config
${springframework.security.version}


org.springframework.security
spring-security-web
${springframework.security.version}




${webappDirectory}/WEB-INF/classes
m2



org.codehaus.mojo
gwt-maven-plugin
2.3.0-1



compile
test
i18n
generateAsync





pl.masl.m2:m2-service

M2.html
${webappDirectory}
pl.masl.m2.web.gwt.client.Constants


pl.masl.m2.web.gwt.client.Messages




org.apache.maven.plugins
maven-war-plugin
2.1.1


compile

exploded




${webappDirectory}



org.apache.maven.plugins
maven-compiler-plugin
2.3.2

${maven.compiler.source}
${maven.compiler.target}
${project.build.sourceEncoding}






Tutaj mamy uzależnienie od bibliotek Springa, bibliotek GWT, bibliotek SmartGWT, ustawień dla kompilacji GWT oraz ustawień dla modułu typu WEB.

Teraz zajmiemy się wciągnięciem modułów do Eclipsa.

Obsługa DTO dla Spring i SmartGWT

, , 0 komentarze

Ostatnio miałem problem z przekazywaniem informacji pomiędzy częścią serwerową aplikacji opartą o serwisy w postaci bean-ów Spring oraz częścią kliencką przygotowaną w formie aplikacji SmartGWT. Problem polegał na tym, że miałem dosyć dużo obiektów domenowych JPA i żeby wymieniać informacje z aplikacją kliencką przy standardowym podejściu musiałbym napisać dla każdej klasy obiektu domenowego klasę DTO. Stwierdziłem, że to strasznie dużo roboty i zacząłem się zastanawiać jak sobie ułatwić życie i zrobić to jakimś automatem. Wpadłem na pomysł przesyłania informacji w postaci tzw. dynamic property i zrobiłem pod to implementację, którą chciałbym się w tym momencie podzielić.

Pomysł polega na tym, aby zarówno po stronie serwerowej jak i klienckiej napisać serializer i deserailzer dynamic property. Rozpocząłem od napisania adnotacji, która ma zasięg metody i służyć będzie do oznaczenie getterów dla property przesyłanych na stronę serwerową i na odwrót. Adnotacja taka ma następującą postać:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DynamicDataProperty {

boolean embeded() default false;
}


No dobrze mamy adnotację to teraz oznaczmy przy jej użyciu odpowiednie obiekty domenowe. Załóżmy, że mamy obiekt domenowy Customer, który wykorzystuje zanurzony obiekt Address tak jak poniżej.

@javax.persistence.Entity
@Table(name = "customers")
public class Customer extends EntityObject {
private static final long serialVersionUID = -5104067194504977189L;

@Column(name = "code", unique = true, nullable = false, length = 60)
private String code;

@Column(name = "name", unique = false, nullable = false, length = 255)
private String name;

@Column(name = "discount", unique = false, nullable = false)
private short discount;

@Embedded
private Address address = new Address();

@DynamicDataProperty
public String getCode() {
return this.code;
}

public void setCode(String code) {
this.code = code;
}

@DynamicDataProperty
public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

@DynamicDataProperty
public short getDiscount() {
return this.discount;
}

public void setDiscount(short discount) {
this.discount = discount;
}

@DynamicDataProperty(embeded = true)
public Address getAddress() {
return this.address;
}

}



@javax.persistence.Embeddable
public class Address implements Serializable {
private static final long serialVersionUID = -2023844913320728453L;

@Column(name = "city", length = 60)
private String city;

@Column(name = "postal_code", length = 10)
private String postalCode;

@Column(name = "street")
private String street;

@Column(name = "local", length = 10)
private String local;

public String getCityPart() {
StringBuffer bf = new StringBuffer();
if (this.postalCode != null) {
bf.append(this.postalCode);
bf.append(" ");
}
if (this.city != null) {
bf.append(this.city);
}
return bf.toString();
}

public String getStreetPart() {
StringBuffer bf = new StringBuffer();
if (this.street != null) {
bf.append(this.street);
}
if (this.local != null) {
bf.append(" ");
bf.append(this.local);
}
return bf.toString();
}

@DynamicDataProperty
public String getFull() {
StringBuffer bf = new StringBuffer();
bf.append(getStreetPart());
if (bf.length() > 0) {
bf.append(", ");
}
bf.append(getCityPart());
return bf.toString();
}

@DynamicDataProperty
public String getCity() {
return this.city;
}

public void setCity(String city) {
this.city = city;
}

@DynamicDataProperty
public String getPostalCode() {
return this.postalCode;
}

public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}

@DynamicDataProperty
public String getStreet() {
return this.street;
}

public void setStreet(String street) {
this.street = street;
}

@DynamicDataProperty
public String getLocal() {
return this.local;
}

public void setLocal(String local) {
this.local = local;
}

}



Jak widać wszystkie gettery zarówno w klasie Customer jak i Address zostały oznaczone adnotacją co powoduje, że wszystkie propery będą przekazywane pomiędzy częścią serwerową i kliencką aplikacji. Dodatkowo getAddress() został oznaczony adnotacją z ustawieniem embeded = true co oznacza, że property dotyczy obiektu zanurzonego, lub inaczej mówiąc obiektu którego serializacja i deserializacja powinna dotyczyć w sensie serializacji jego property.

Teraz tworzymy dwie klasy obiekty których będą przesyłane pomiędzy częścią serwerową i kliencką. Są to klasy do przesyłania zserializowanej postaci obiektów domenowych, czyli swojego rodzaju generyczne DTO. Tak więc mamy klasę konetenerową:

public class DynamicData extends AbstractData {
private static final long serialVersionUID = -2549646020563019965L;

private LinkedHashMap map = new LinkedHashMap();

public LinkedHashMap getMap() {
return this.map;
}

@Override
public Long getId() {
DataProperty prop = this.map.get("id");
return (Long) (prop != null ? prop.getValue() : null);
}
}


i klasę komponentową:

public class DataProperty implements Serializable {
private static final long serialVersionUID = -4627984997682982978L;

private DataPropertyType type = DataPropertyType.STRING;
private String str;
private Integer integer;
private Short shortv;
private Long longv;
private Boolean boolv;
private DynamicData embebed;

public DataProperty() {
this("");
}

public DataProperty(Object value) {
if (value == null) {
throw new IllegalArgumentException("Value cannot be null");
}
if (value instanceof String) {
this.str = (String) value;
} else if ((value instanceof DynamicData)) {
this.embebed = (DynamicData) value;
this.type = DataPropertyType.EMBEDED;
} else if ((value instanceof Boolean)) {
this.boolv = (Boolean) value;
this.type = DataPropertyType.BOOL;
} else if ((value instanceof Short)) {
this.shortv = ((Short) value);
this.type = DataPropertyType.SHORT;
} else if ((value instanceof Integer)) {
this.integer = (Integer) value;
this.type = DataPropertyType.INT;
} else if (value instanceof Long) {
this.longv = (Long) value;
this.type = DataPropertyType.LONG;
} else {
this.str = value.toString();
}
}

public Object getValue() {
switch (this.type) {
case EMBEDED:
return this.embebed;
case BOOL:
return this.boolv;
case SHORT:
return this.shortv;
case INT:
return this.integer;
case LONG:
return this.longv;
default:
return this.str;
}
}

public DataPropertyType getType() {
return this.type;
}
}


public enum DataPropertyType implements Serializable {

STRING,
INT,
LONG,
BOOL,
EMBEDED,
SHORT;
}



Jak widać w klasie komponentowej każdy typ ma swoje pole. Można by zapytać dlaczego nie zrobić jednego pola typu Object, w którym zapisana była by informacja o zserializowanej wartości. Rzeczywiście tak by było najprościej, ale niestety jest to problematyczne z racji wykorzystania GWT. Ponieważ obiekty komponentowe będą przesyłane na stronę kliencką muszą być skompilowane przez GWT do javasrcipt-u i jeżeli damy Obiect kompilator będzie próbował stworzyć generyczny javascript i tu dostaniemy albo warningi przy kompilacji albo problemy przy serializacji w runtime. Dlatego najlepiej jest wyręczyć kompilator GWT i samemu przygotować implementację opierająca się o jasno określone typy serializowanych wartości.

No dobrze mamy klasy domenowe, mamy komponenty umożliwiające nam przekazywanie informacji pomiędzy częścią serwerową i kliencką teraz potrzebujemy serializatorów. Serializator po stronie klienckiej ma następującą postać:

public class DynamicDataMapper {
private static final Logger log = LoggerFactory.getLogger(DynamicDataMapper.class);

private DynamicDataMapper() {
}

public static void map(DynamicData data, T entity) {
if ((data != null) && (entity != null)) {
try {
to(data, entity);
} catch (Exception e) {
throw new MappingException(e);
}
}
}

public static DynamicData map(T entity) {
DynamicData data = null;
if (entity != null) {
data = new DynamicData();
try {
from(data, entity);
} catch (Exception e) {
throw new MappingException(e);
}
}
return data;
}

private static void to(DynamicData data, Object inst) throws Exception {
PropertyDescriptor[] desc = ReflectUtils.getBeanProperties(inst.getClass());
Map propMap = new HashMap();
for (PropertyDescriptor d : desc) {
propMap.put(d.getName(), d);
}
LinkedHashMap map = data.getMap();
for (Entry entry : map.entrySet()) {
String key = entry.getKey();
try {
DataProperty prop = entry.getValue();
PropertyDescriptor d = propMap.get(key);
if (d != null) {
Object value = prop != null ? prop.getValue() : null;
//log.debug("PROP: {}, VALUE: {}", key, value);
if (value instanceof DynamicData) {
Method mth = d.getReadMethod();
if (mth != null) {
Object bean = mth.invoke(inst, new Object[0]);
if (bean != null) {
to((DynamicData) value, bean);
}
}
} else {
Method mth = d.getWriteMethod();
if (mth != null) {
//Class clas = mth.getParameterTypes()[0];
//log.debug("Parameter class: {}", clas);
mth.invoke(inst, value);
}
}
}
} catch (Exception ex) {
throw new MappingException("Error for Parameter " + key, ex);
}
}
}

private static void from(DynamicData data, Object inst) throws Exception {
PropertyDescriptor[] desc = ReflectUtils.getBeanProperties(inst.getClass());
for (PropertyDescriptor des : desc) {
Method mth = des.getReadMethod();
if (mth != null) {
DynamicDataProperty an = mth.getAnnotation(DynamicDataProperty.class);
if (an != null) {
String key = des.getName();
Object result = mth.invoke(inst, new Object[0]);
DataProperty prop = null;
if (result != null) {
if (an.embeded()) {
DynamicData embeded = new DynamicData();
from(embeded, result);
result = embeded;
}
prop = new DataProperty(result);
}
data.getMap().put(key, prop);
}
}
}
}

}


Serializator ma dwie metody statyczne from i to służące do serializacji i deserializacji przekazywanych komponentów. Serializacja polega na zapisaniu property z obiektu domenowego w postaci mapy property, gdzie kluczem jest nazwa a wartością obiekt komponentowy DataProperty zawierający wartość odczytaną z obiektu domenowego. W przypadku property typu embeded tworzony nowy obiekt DynamicData i zapisywany jako wartość w odpowiednim DataProperty.

No dobrze mamy część serwerową no to czas na część kliencką i oto i ona:

public class DynamicDataSource extends TableDataSource {

private static final Converter CONVERTER = new Converter();

public DynamicDataSource(DataServiceAsync service, SortSpecifier[] sort) {
super(service, sort);
}

public DynamicDataSource(DataServiceAsync service) {
super(service);
}

@Override
public IConverter getRecordConverter() {
return CONVERTER;
}

static class Converter implements IConverter {

@Override
public void convertData(Record rec, DynamicData element) {
this.convertData(rec, null, element);
}

@Override
public DynamicData convertRecord(Record rec) {
DynamicData data = new DynamicData();
String[] attrs = rec.getAttributes();
for (String attr : attrs) {
if (Log.isDebugEnabled()) {
Log.debug("Processing attribute: " + attr);
}
LinkedHashMap map = getMap(attr, data);
Object value = rec.getAttributeAsObject(attr);
int idx = attr.lastIndexOf('.');
if (idx > -1) {
attr = attr.substring(idx + 1);
}
DataProperty dp = null;
if (value != null) {
if (value instanceof JavaScriptObject) {
value = JSOHelper.convertToJava((JavaScriptObject) value);
}
if (Log.isDebugEnabled()) {
Log.debug("VALUE calss: " + value.getClass() + " - " + value);
}
dp = new DataProperty(value);
}
map.put(attr, dp);
}
return data;
}

private LinkedHashMap getMap(String attr, DynamicData data) {
LinkedHashMap map = data.getMap();
int idx = attr.indexOf('.');
if (idx > -1) {
String at = attr.substring(0, idx);
DataProperty dp = map.get(at);
if (dp == null) {
DynamicData md = new DynamicData();
dp = new DataProperty(md);
map.put(at, dp);
}
return getMap(attr.substring(idx + 1), (DynamicData) dp.getValue());
}
return map;
}

private void convertData(Record rec, String prop, DynamicData element) {
LinkedHashMap map = element.getMap();
for (Entry entry : map.entrySet()) {
Object value = null;
String property = prop != null ? prop + "." + entry.getKey() : entry.getKey();
DataProperty dp = entry.getValue();
if (dp != null) {
value = dp.getValue();
}
if (value instanceof DynamicData) {
convertData(rec, property, (DynamicData) value);
} else {
rec.setAttribute(property, value);
}
}
}

}
}


Część kliencka jest w postaci DataSource i mamy tutaj metodę convertData() która służy do konwersji obiektu generycznego DTO na atrybuty w rekordzie DataSource-a oraz metodę convertRecord(), która konwertuje dane z rekordu na odpowiedni obiekt generyczny DTO.

Lunux - karta TV: przydatne komendy

, środa, 9 marca 2011 0 komentarze

Pokaż załadowane moduły ograniczając się do cx

sudo lsmod | grep cx


cx8800 29418 0
cx88xx 74190 1 cx8800
rc_core 17007 7 ir_lirc_codec,ir_sony_decoder,ir_jvc_decoder,ir_rc6_decoder,ir_rc5_decoder,ir_nec_decoder,cx88xx
i2c_algo_bit 5205 1 cx88xx
tveeprom 13145 1 cx88xx
v4l2_common 8312 3 tuner,cx8800,cx88xx
videodev 69118 4 tuner,cx8800,cx88xx,v4l2_common
videobuf_dma_sg 8668 2 cx8800,cx88xx
videobuf_core 16044 3 cx8800,cx88xx,videobuf_dma_sg
btcx_risc 3738 2 cx8800,cx88xx
i2c_core 27089 11 tuner_simple,tea5767,nvidia,tuner,cx8800,cx88xx,i2c_algo_bit,tveeprom,v4l2_common,videodev,i2c_i801


Wyświetl bufor warstwy jądra ograniczając się do wpisów zawierających cx
dmesg | grep cx


[ 11.199826] cx88/0: cx2388x v4l2 driver version 0.0.8 loaded
[ 11.199872] cx8800 0000:05:02.0: PCI INT A -> GSI 18 (level, low) -> IRQ 18
[ 11.202933] cx88[0]: subsystem: 0000:0000, board: PixelView [card=3,insmod option], frontend(s): 0
[ 11.202937] cx88[0]: TV tuner type 23, Radio tuner type 23
[ 11.202939] cx88[0]: cx88_reset
[ 11.314193] cx88[0]: Test OK
[ 11.392886] cx88[0]: i2c scan: found device @ 0xc0 [tuner (analog)]
[ 12.606248] tuner 1-0060: chip found @ 0xc0 (cx88[0])
[ 12.641231] cx88[0]/0: found at 0000:05:02.0, rev: 3, irq: 18, latency: 32, mmio: 0xf9000000
[ 12.641671] cx88[0]/0: registered device video0 [v4l2]
[ 12.641883] cx88[0]/0: registered device vbi0
[ 12.642005] cx88[0]/0: registered device radio0
[ 12.642052] cx88[0]: set_tvnorm: "NTSC-M" fsc8=28636360 adc=28636363 vdec=28636360 db/dr=28636360/28636360
[ 12.642058] cx88[0]: set_pll: MO_PLL_REG 0x00fffffe [old=0x00f15f18,freq=28636360]
[ 12.642063] cx88[0]: pll locked [pre=2,ofreq=28636360]
[ 12.642067] cx88[0]: set_tvnorm: MO_INPUT_FORMAT 0x00000001 [old=0x00000007]
[ 12.642073] cx88[0]: set_tvnorm: MO_OUTPUT_FORMAT 0x181f0008 [old=0x181f0000]
[ 12.642078] cx88[0]: set_tvnorm: MO_SCONV_REG 0x00020000 [old=0x00021f07]
[ 12.642082] cx88[0]: set_tvnorm: MO_SUB_STEP 0x00400000 [old=0x0043e0f8]
[ 12.642087] cx88[0]: set_tvnorm: MO_SUB_STEP_DR 0x00400000 [old=0x00538e38]
[ 12.642092] cx88[0]: set_tvnorm: MO_AGC_BURST 0x00007270 [old=0x00006d63,bdelay=114,agcdelay=112]
[ 12.642097] cx88[0]: set_tvnorm: MO_HTOTAL 0x0000038e [old=0x0000135a,htotal=910]
[ 12.642100] cx88[0]: set_scale: 320x240 [TB,NTSC-M]
[ 12.642103] cx88[0]: set_scale: hdelay 0x0038 (width 754)
[ 12.642105] cx88[0]: set_scale: hscale 0x15b3
[ 12.642107] cx88[0]: set_scale: hactive 0x0140
[ 12.642109] cx88[0]: set_scale: vdelay 0x0018
[ 12.642111] cx88[0]: set_scale: vscale 0x1e00
[ 12.642113] cx88[0]: set_scale: vactive 0x01e0
[ 12.642115] cx88[0]: set_scale: filter 0x80009
[ 12.642121] cx88[0]/0: set_audio_standard_BTSC (status: known-good)
[ 12.643011] cx88[0]/0: set_control id=0x980900(Brightness) ctrl=0x7f, reg=0x310110 val=0xff (mask 0xff)
[ 12.643056] cx88[0]/0: set_control id=0x980901(Contrast) ctrl=0x3f, reg=0x310110 val=0x3f00 (mask 0xff00)
[ 12.643063] cx88[0]/0: set_control id=0x980903(Hue) ctrl=0x7f, reg=0x310118 val=0xff (mask 0xff)
[ 12.643069] cx88[0]/0: set_control id=0x980902(Saturation) ctrl=0x7f, reg=0x310114 val=0x5a7f (mask 0xffff)
[ 12.643074] cx88[0]/0: set_control id=0x98091D(Chroma AGC) ctrl=0x01, reg=0x310104 val=0x400 (mask 0x400)
[ 12.643079] cx88[0]/0: set_control id=0x98091E(Color killer) ctrl=0x01, reg=0x310104 val=0x200 (mask 0x200)
[ 12.643084] cx88[0]/0: set_control id=0x980909(Mute) ctrl=0x01, reg=0x320594 val=0x40 (mask 0x40) [shadowed]
[ 12.643088] cx88[0]/0: set_control id=0x980905(Volume) ctrl=0x3f, reg=0x320594 val=0x00 (mask 0x3f) [shadowed]
[ 12.643092] cx88[0]/0: set_control id=0x980906(Balance) ctrl=0x40, reg=0x320598 val=0x00 (mask 0x7f) [shadowed]
[ 12.643096] cx88[0]/0: video_mux: 0 [vmux=0,gpio=0xff00,0x0,0x0,0x0]
[ 12.647184] cx88[0]/0: cx88: tvaudio thread started
[ 12.664972] cx88[0]/0: open dev=vbi0 radio=0 type=vbi-cap
[ 12.665165] cx88[0]/0: open dev=radio0 radio=1 type=(null)
[ 12.665169] cx88[0]/0: video_open: setting radio device
[ 12.665173] cx88[0]/0: set_audio_standard_FM (status: unknown)
[ 12.665197] cx88[0]/0: cx88_set_stereo: mask 0x3f, ctl 0x1a [status=0x22,ctl=0x101a,vol=0x40]
[ 12.666213] cx88[0]/0: open dev=video0 radio=0 type=vid-cap
[ 27.217735] cx88[0]/0: open dev=radio0 radio=1 type=(null)
[ 27.217742] cx88[0]/0: video_open: setting radio device
[ 27.217745] cx88[0]/0: set_audio_standard_FM (status: unknown)
[ 27.217769] cx88[0]/0: cx88_set_stereo: mask 0x3f, ctl 0x1a [status=0x22,ctl=0x101a,vol=0x40]
[ 27.220711] cx88[0]/0: open dev=vbi0 radio=0 type=vbi-cap
[ 27.222937] cx88[0]/0: open dev=video0 radio=0 type=vid-cap
[ 239.487766] cx88[0]/0: open dev=video0 radio=0 type=vid-cap


Wyświetl karty dźwiękowe
cat /proc/asound/cards


0 [Intel ]: HDA-Intel - HDA Intel
HDA Intel at 0xfb000000 irq 45
1 [Audigy ]: Audigy - SB Audigy 1 [SB0092]
SB Audigy 1 [SB0092] (rev.3, serial:0x531102) at 0xd000, irq 20


Wyświetl listę urządzeń typu audio
aplay -l


**** List of PLAYBACK Hardware Devices ****
card 0: Intel [HDA Intel], device 0: ALC888 Analog [ALC888 Analog]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: Intel [HDA Intel], device 1: ALC888 Digital [ALC888 Digital]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: Audigy [SB Audigy 1 [SB0092]], device 0: emu10k1 [ADC Capture/Standard PCM Playback]
Subdevices: 32/32
Subdevice #0: subdevice #0
Subdevice #1: subdevice #1
Subdevice #2: subdevice #2
Subdevice #3: subdevice #3
Subdevice #4: subdevice #4
Subdevice #5: subdevice #5
Subdevice #6: subdevice #6
Subdevice #7: subdevice #7
Subdevice #8: subdevice #8
Subdevice #9: subdevice #9
Subdevice #10: subdevice #10
Subdevice #11: subdevice #11
Subdevice #12: subdevice #12
Subdevice #13: subdevice #13
Subdevice #14: subdevice #14
Subdevice #15: subdevice #15
Subdevice #16: subdevice #16
Subdevice #17: subdevice #17
Subdevice #18: subdevice #18
Subdevice #19: subdevice #19
Subdevice #20: subdevice #20
Subdevice #21: subdevice #21
Subdevice #22: subdevice #22
Subdevice #23: subdevice #23
Subdevice #24: subdevice #24
Subdevice #25: subdevice #25
Subdevice #26: subdevice #26
Subdevice #27: subdevice #27
Subdevice #28: subdevice #28
Subdevice #29: subdevice #29
Subdevice #30: subdevice #30
Subdevice #31: subdevice #31
card 1: Audigy [SB Audigy 1 [SB0092]], device 2: emu10k1 efx [Multichannel Capture/PT Playback]
Subdevices: 8/8
Subdevice #0: subdevice #0
Subdevice #1: subdevice #1
Subdevice #2: subdevice #2
Subdevice #3: subdevice #3
Subdevice #4: subdevice #4
Subdevice #5: subdevice #5
Subdevice #6: subdevice #6
Subdevice #7: subdevice #7
card 1: Audigy [SB Audigy 1 [SB0092]], device 3: emu10k1 [Multichannel Playback]
Subdevices: 1/1
Subdevice #0: subdevice #0


Wyświetl urządzenia PCI ze szczegółami
lspci -vvv


05:00.2 FireWire (IEEE 1394): Creative Labs SB Audigy FireWire Port (prog-if 10 [OHCI])
Subsystem: Creative Labs SB Audigy FireWire Port
Flags: bus master, medium devsel, latency 32, IRQ 19
Memory at fa005000 (32-bit, non-prefetchable) [size=2K]
Memory at fa000000 (32-bit, non-prefetchable) [size=16K]
Capabilities: [44] Power Management version 2
Kernel driver in use: firewire_ohci
Kernel modules: firewire-ohci

05:01.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+ (rev 10)
Subsystem: Edimax Computer Co. Device 9503
Flags: bus master, medium devsel, latency 64, IRQ 19
I/O ports at d200 [size=256]
Memory at fa004000 (32-bit, non-prefetchable) [size=256]
Capabilities: [50] Power Management version 2
Kernel driver in use: 8139too
Kernel modules: 8139too, 8139cp

05:02.0 Multimedia video controller: Conexant Systems, Inc. CX23880/1/2/3 PCI Video and Audio Decoder (rev 03)
Flags: bus master, medium devsel, latency 32, IRQ 18
Memory at f9000000 (32-bit, non-prefetchable) [size=16M]
Capabilities: [44] Vital Product Data
Capabilities: [4c] Power Management version 2
Kernel driver in use: cx8800
Kernel modules: cx8800

Linux - jak sprawdzić otwarte porty

środa, 23 lutego 2011 0 komentarze

W celu sprawdzenia otwartych portów należy wykonać komendę:

netstat -tulpn


Znaczenie opcji:
- t - porty TCP/IP
- u - porty UDP
- l - tylko porty typu LISTEN
- p - pokaż PID procesu związanego z otwartym portem
- n -pokazuje adresy numeryczne; nie próbuje zdeterminować symbolicznego hosta, portu czy nazwy użytkownika

FEDORA CORE 14 Linux - Konfiguracja karty TV: PixelView PlayTV HD

, wtorek, 15 lutego 2011 0 komentarze

Nadszedł czas na skonfigurowanie karty telewizyjnej na mojej Fedorze. Niestety na linux-ie konfiguracja nie jest tak prosta jak na Windozie i trochę czasu trzeba zaplanować żeby ten temat załatwić na swoim kompie wyposarzonym w linux-a. Mam nadzieję, że to co udało mi się zrobić i opisać niżej pomoże innym w konfiguracji karty TV i pozwoli na zaoszczędzenie czasu na Googlowanie w nadzieji, że coś się uda znaleźć żeby działało.

Opiszę tutaj moją konfigurację dla karty TV, którą aktualnie posiadam, czyli PixelView PlayTV HD opartej na chipie CX2388x.

Po pierwsze należy pobrać bieżącą wersję pliku pci.ids z dystrybucji i zainstalować co robimy komendą:

sudo update-pciids

Następnie sprawdzamy czy karta TV jest rozpoznawana przez system wyświetlając wszystkie podłączone urządzenia PCI:
lspci

U mnie jest to następujący wpis w liście wyświetlonej po wykonaniu komendy:
05:02.0 Multimedia video controller: Conexant Systems, Inc. CX23880/1/2/3 PCI Video and Audio Decoder (rev 03)
Teraz sprawdzamy czy w systemie jest załadowany odpowiedni sterownik w postaci modułu video4linux. W celu sprawdzenia czy odpowiedni sterownik jest załadowany w systemie należy wykonać komendę:
dmesg | grep cx88

Po wykonaniu tej komendy pewnie na ekranie pojawiła się lista na której końcu są następujące linie:

[ 11.307744] cx88[0]: subsystem: 0000:0000, board: UNKNOWN/GENERIC [card=0,autodetected], frontend(s): 0
[ 11.307747] cx88[0]: TV tuner type -1, Radio tuner type -1
[ 12.636927] tuner 1-0060: chip found @ 0xc0 (cx88[0])
[ 12.663271] cx88[0]/0: found at 0000:05:02.0, rev: 3, irq: 18, latency: 32, mmio: 0xf9000000
[ 12.663464] cx88[0]/0: registered device video0 [v4l2]
[ 12.663539] cx88[0]/0: registered device vbi0

Oznacza to, że system nie wykrył automatycznie typu karty i trzeba mu podpowiedzieć jaką mamy kartę i jej ustawienia. Teraz zaczyna się zwykle droga przez mękę polegająca na określeniu jaki to rodzaj karty posiadamy i wykonujemy to medodą prób i błędów wykonując następującą sekwencję komend:

sudo rmmod cx8800
sudo rmmod cx88xx
sudo modprobe cx88xx card=cx tuner=tx
sudo modprobe cx8800

gdzie cx - oznacza model karty TV, a tx - oznacza model tunera TV (lista typów kart i tunerów podana jest niżej).
W przypadku karty, której dotyczy opis ustawienia które powinny działać są następujące:
sudo modprobe cx88xx card=3 tuner=5 radio=5

Teraz przydało by się sprawdzić czy to działa. Po pierwsze wykonujemy komendę:
dmesg | grep cx88

co powinno nam dać w rezultacie mniej więcej następujący wynik:

[ 1358.792181] cx88/0: cx2388x v4l2 driver version 0.0.8 loaded
[ 1358.792311] cx8800 0000:05:02.0: PCI INT A -> GSI 18 (level, low) -> IRQ 18
[ 1358.795258] cx88[0]: subsystem: 0000:0000, board: PixelView [card=3,insmod option], frontend(s): 0
[ 1358.795261] cx88[0]: TV tuner type 5, Radio tuner type -1
[ 1358.911478] tuner 1-0060: chip found @ 0xc0 (cx88[0])
[ 1358.930185] cx88[0]/0: found at 0000:05:02.0, rev: 3, irq: 18, latency: 32, mmio: 0xf9000000
[ 1358.930306] cx88[0]/0: registered device video0 [v4l2]
[ 1358.930345] cx88[0]/0: registered device vbi0
[ 1358.930382] cx88[0]/0: registered device radio0

Następnie musimy sobie zainstalować program do oglądania telewizji. Ja używam w tym celu programu tvtime, na podstawie którego oprę dalszy opis.
No dobrze jeżeli mamy zainstalowany tvtime dobrze jest wykonać komendę:
tvtime-scanner

Komenda ta pozwala na wyszukanie kanałów po czym to co zostanie znalezione jest zapisywane w następującym pliku /home/myhome/.tvtime/stationlist.xml. W moim przypadku w wyniku działania przeszukania kanałów otrzymałem następującą zawartość pliku:













Oczywiście skaner nie wpisuje nazw kanałów to po wyp¶óbowaniu należy sobie uzupełnić samemu. Dodatkowo skaner może wyszukać kanały kilkukrotnie wtedy usunięcie polega na usunięciu odpowienich linii z pliku. Kolejność kanałów określa parametr position.
Dobrze teraz włączamy program tvtime. Po włączeniu powinniśmy już widzieć obraz z kanałów, jeżeli nie widać nic i jest niebieski obraz należy najprawdopodomniej zmienić źródło. Robimy to naciskając prawy klawisz myszy i dalej Ustawienia wejścia -> Zmień Źródło obrazu. U mnie jest to wartość Television. Można też dowolnie zmianiać inne ustawienia tej sekcji - proponuję popróbować lub poszukać na sieci co oznaczają poszczególne ustawienia.

Dobrze, najprawdopodobniej jest tak że mamy obraz i niestety nie mamy dźwięku. Tutaj następne schody. Ja mam u siebie podłączoną kartę telewizyjną bezpośrednio kablem do karty dźwiękowej czyli tzw. podłączenie typu "internal audio cable" i tutaj skupię się na przepisie jak uzyskać dźwięk w tego typu konfiguracji. Należy w tym przypadku wejść w ustawienia ALSA poprzez uruchomienie komendy:
alsamixer

Następnie zmieniamy kartę na odpowiednią (ja mam dwie karty dźwiekowe) używając klawisza funkcyjnego . Teraz przechodząc strzałkami kursora lokalizujemy Aux i zwiększamy głośność np. do 100% (klawisz kursora w górę). Najprawdopodoniej ten kanał jest również wyciszony, więc naciskamy klawisz . Przechodzimy dalej klawiszem kursora (w prawo) do kanału Analog M i zwiększamy głośność odpowiednio. U mnie wykonanie tych czynności spowodowało pojawienie się dźwięku. Z alsamixer wychodzimy przy użyciu .

Teraz wszystko powinno być OK, natomiast nie chcemy po każdym logowaniu wykonywać komend w celu aktywacji modułów i wprowadzania ustawień, dlatego należy w katalogu /etc/modprobe.d utworzyć plik z rozszerzeniem .conf np. tv.conf i wpisać do niego następującą zawartość:

alias char-major-89-* i2c-dev
options i2c-algo-bit bit_test=1

alias char-major-81 videodev
alias char-major-81-0 cx88xx
options cx88xx card=3 tuner=23 radio=23 i2c_scan=1 nicam=1 dsp_debug=1 always_analog=1 core_debug=1
alias char-major-81-0 cx8800
options cx8800 video_debug=1

Znaczenie poszczególnych wpisów proponuję poszukać sobie na stronach internetowych, ogólnie natomiast rzecz biorąc powyższe wpisy powodują odpowiednie załadowanie modułów oraz wprowadzenie odpowiednich ustawień dla tych modułów. W celu uzyskania informacji jakie parametry są dopuszczalne dla poszczególnych modułów proponuję użyć komendy modinfo np.
modinfo cx88xx


Należy pamiętać, że przed wprowadzeniem automatycznego ładowania modułów i ustawienia opcji dla nich najlepiej wykonać próby przy uzyciu rmmod i modprobe to może ustrzec od kłopotów na starcie systemu.


Lista typów kart:

1 -> Hauppauge WinTV 34xxx models [0070:3400,0070:3401]
2 -> GDI Black Gold [14c7:0106,14c7:0107]
3 -> PixelView [1554:4811]
4 -> ATI TV Wonder Pro [1002:00f8]
5 -> Leadtek Winfast 2000XP Expert [107d:6611,107d:6613]
6 -> AverTV Studio 303 (M126) [1461:000b]
7 -> MSI TV-@nywhere Master [1462:8606]
8 -> Leadtek Winfast DV2000 [107d:6620]
9 -> Leadtek PVR 2000 [107d:663b,107d:663c,107d:6632]
10 -> IODATA GV-VCP3/PCI [10fc:d003]
11 -> Prolink PlayTV PVR
12 -> ASUS PVR-416 [1043:4823,1461:c111]
13 -> MSI TV-@nywhere
14 -> KWorld/VStream XPert DVB-T [17de:08a6]
15 -> DViCO FusionHDTV DVB-T1 [18ac:db00]
16 -> KWorld LTV883RF
17 -> DViCO FusionHDTV 3 Gold-Q [18ac:d810,18ac:d800]
18 -> Hauppauge Nova-T DVB-T [0070:9002,0070:9001,0070:9000]
19 -> Conexant DVB-T reference design [14f1:0187]
20 -> Provideo PV259 [1540:2580]
21 -> DViCO FusionHDTV DVB-T Plus [18ac:db10,18ac:db11]
22 -> pcHDTV HD3000 HDTV [7063:3000]
23 -> digitalnow DNTV Live! DVB-T [17de:a8a6]
24 -> Hauppauge WinTV 28xxx (Roslyn) models [0070:2801]
25 -> Digital-Logic MICROSPACE Entertainment Center (MEC) [14f1:0342]
26 -> IODATA GV/BCTV7E [10fc:d035]
27 -> PixelView PlayTV Ultra Pro (Stereo)
28 -> DViCO FusionHDTV 3 Gold-T [18ac:d820]
29 -> ADS Tech Instant TV DVB-T PCI [1421:0334]
30 -> TerraTec Cinergy 1400 DVB-T [153b:1166]
31 -> DViCO FusionHDTV 5 Gold [18ac:d500]
32 -> AverMedia UltraTV Media Center PCI 550 [1461:8011]
33 -> Kworld V-Stream Xpert DVD
34 -> ATI HDTV Wonder [1002:a101]
35 -> WinFast DTV1000-T [107d:665f]
36 -> AVerTV 303 (M126) [1461:000a]
37 -> Hauppauge Nova-S-Plus DVB-S [0070:9201,0070:9202]
38 -> Hauppauge Nova-SE2 DVB-S [0070:9200]
39 -> KWorld DVB-S 100 [17de:08b2]
40 -> Hauppauge WinTV-HVR1100 DVB-T/Hybrid [0070:9400,0070:9402]
41 -> Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile) [0070:9800,0070:9802]
42 -> digitalnow DNTV Live! DVB-T Pro [1822:0025,1822:0019]
43 -> KWorld/VStream XPert DVB-T with cx22702 [17de:08a1,12ab:2300]
44 -> DViCO FusionHDTV DVB-T Dual Digital [18ac:db50,18ac:db54]
45 -> KWorld HardwareMpegTV XPert [17de:0840]
46 -> DViCO FusionHDTV DVB-T Hybrid [18ac:db40,18ac:db44]
47 -> pcHDTV HD5500 HDTV [7063:5500]
48 -> Kworld MCE 200 Deluxe [17de:0841]
49 -> PixelView PlayTV P7000 [1554:4813]
50 -> NPG Tech Real TV FM Top 10 [14f1:0842]
51 -> WinFast DTV2000 H [107d:665e]
52 -> Geniatech DVB-S [14f1:0084]
53 -> Hauppauge WinTV-HVR3000 TriMode Analog/DVB-S/DVB-T [0070:1404,0070:1400,0070:1401,0070:1402]
54 -> Norwood Micro TV Tuner
55 -> Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM [c180:c980]
56 -> Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder [0070:9600,0070:9601,0070:9602]


Lista typów tunerów:

tuner=0 - Temic PAL (4002 FH5)
tuner=1 - Philips PAL_I (FI1246 and compatibles)
tuner=2 - Philips NTSC (FI1236,FM1236 and compatibles)
tuner=3 - Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)
tuner=4 - NoTuner
tuner=5 - Philips PAL_BG (FI1216 and compatibles)
tuner=6 - Temic NTSC (4032 FY5)
tuner=7 - Temic PAL_I (4062 FY5)
tuner=8 - Temic NTSC (4036 FY5)
tuner=9 - Alps HSBH1
tuner=10 - Alps TSBE1
tuner=11 - Alps TSBB5
tuner=12 - Alps TSBE5
tuner=13 - Alps TSBC5
tuner=14 - Temic PAL_BG (4006FH5)
tuner=15 - Alps TSCH6
tuner=16 - Temic PAL_DK (4016 FY5)
tuner=17 - Philips NTSC_M (MK2)
tuner=18 - Temic PAL_I (4066 FY5)
tuner=19 - Temic PAL* auto (4006 FN5)
tuner=20 - Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)
tuner=21 - Temic NTSC (4039 FR5)
tuner=22 - Temic PAL/SECAM multi (4046 FM5)
tuner=23 - Philips PAL_DK (FI1256 and compatibles)
tuner=24 - Philips PAL/SECAM multi (FQ1216ME)
tuner=25 - LG PAL_I+FM (TAPC-I001D)
tuner=26 - LG PAL_I (TAPC-I701D)
tuner=27 - LG NTSC+FM (TPI8NSR01F)
tuner=28 - LG PAL_BG+FM (TPI8PSB01D)
tuner=29 - LG PAL_BG (TPI8PSB11D)
tuner=30 - Temic PAL* auto + FM (4009 FN5)
tuner=31 - SHARP NTSC_JP (2U5JF5540)
tuner=32 - Samsung PAL TCPM9091PD27
tuner=33 - MT20xx universal
tuner=34 - Temic PAL_BG (4106 FH5)
tuner=35 - Temic PAL_DK/SECAM_L (4012 FY5)
tuner=36 - Temic NTSC (4136 FY5)
tuner=37 - LG PAL (newer TAPC series)
tuner=38 - Philips PAL/SECAM multi (FM1216ME MK3)
tuner=39 - LG NTSC (newer TAPC series)
tuner=40 - HITACHI V7-J180AT
tuner=41 - Philips PAL_MK (FI1216 MK)
tuner=42 - Philips 1236D ATSC/NTSC dual in
tuner=43 - Philips NTSC MK3 (FM1236MK3 or FM1236/F)
tuner=44 - Philips 4 in 1 (ATI TV Wonder Pro/Conexant)
tuner=45 - Microtune 4049 FM5
tuner=46 - Panasonic VP27s/ENGE4324D
tuner=47 - LG NTSC (TAPE series)
tuner=48 - Tenna TNF 8831 BGFF)
tuner=49 - Microtune 4042 FI5 ATSC/NTSC dual in
tuner=50 - TCL 2002N
tuner=51 - Philips PAL/SECAM_D (FM 1256 I-H3)
tuner=52 - Thomson DTT 7610 (ATSC/NTSC)
tuner=53 - Philips FQ1286
tuner=54 - tda8290+75
tuner=55 - TCL 2002MB
tuner=56 - Philips PAL/SECAM multi (FQ1216AME MK4)
tuner=57 - Philips FQ1236A MK4
tuner=58 - Ymec TVision TVF-8531MF/8831MF/8731MF
tuner=59 - Ymec TVision TVF-5533MF
tuner=60 - Thomson DTT 761X (ATSC/NTSC)
tuner=61 - Tena TNF9533-D/IF/TNF9533-B/DF
tuner=62 - Philips TEA5767HN FM Radio
tuner=63 - Philips FMD1216ME MK3 Hybrid Tuner
tuner=64 - LG TDVS-H06xF
tuner=65 - Ymec TVF66T5-B/DFF
tuner=66 - LG TALN series
tuner=67 - Philips TD1316 Hybrid Tuner
tuner=68 - Philips TUV1236D ATSC/NTSC dual in
tuner=69 - Tena TNF 5335 and similar models
tuner=70 - Samsung TCPN 2121P30A
tuner=71 - Xceive xc3028
tuner=72 - Thomson FE6600
tuner=73 - Samsung TCPG 6121P30A



-

FEDORA CORE 14 Linux - Konfiguracja L2TP/IPsec

, piątek, 11 lutego 2011 0 komentarze

Chciałbym przedstawić sposób na skonfigurowanie połączenia L2TP/IPsec w Fedorze. Trochę się z tym tematem ostatnio pomęczyłem i jeżeli kouś to pomoże to przedstawię tutaj procedurę "krok po kroku" jak to wykonać.

No to zacznijmy od początku, czyli od ściągnięcia odpowiednich pakietów, czyli:
1. OpenSWAN
2. xl2tpd

Następnie zabieramy się za przygotowanie klucza i bazy kluczy NSS. Zakładam, że w posiadaniu jest klucz w postaci pkcs12, jeżeli nie a mamy 2 certyfikaty i klucz prywatny w osobnych plikach tekstowych (format PEM) to należy wykonać następującą komendę do stworzenia klucza pkcs12:

openssl pkcs12 -export -in cert.pem -inkey user.key -certfile cacert.pem -out certkey.p12 -name 'masl-12'

Oczywiście w powyższej instrukcji można użyć dowolnej nazwy, w tym przypadku należy zmianić 'masl-12' na dowolnie wybrany ciąg znaków. Po naciśnięciu w terminalu nastąpi prośba o podanie hasła do klucza prywatnego ("Enter pass phrase for user.key:"), które to hasło należy mieć i w tym momencie wpisać. Po wpisaniu poprawnym hasła powinno nastąpić pytanie o hasło exportu ("Enter Export Password:") tutaj wpisujemy dowolne hasło i po potwierdzamy wpisując je ponownie. Po wykonaniu powyżej opisanych króków powinein zostać utworzony w bieżącym katalogu plik o nazwie 'certkey.p12'.

Teraz plik z danymi PKCS#12 utworzony w poprzednim kroku należy zaimportować do bazy kluczy i certyfikatów. Wcześniej jednak musimy sobie stworzyć nową bazę (lub raczej nowe bazy) i robimy to przy pomocy następujacej komendy:
su -c "certutil -N -d /etc/ipsec.d"

Po jej wywołaniu zostaniemy poproszeni o podanie hasła ROOT-a (wpisujemy odpowiednie hasło) a następnie o nowe hasło i jego powtórzenie ("Enter new password:", "Re-enter password:") do nowotworzonej bazy kluczy. Można to hasło pozostawić puste poprzez wykonanie bez podawania czegokolwiek. Po tej operacji powinny zostać utworzone następujące pliki w katalogu '/etc/ipsec.d': cert8.db, key3.db, secmod.db.

Teraz import pliku z danymi PKCS#12 do nowoutworzonej bazy kluczy, wykonujemy następującą komendą:
su -c "pk12util -i certkey.p12 -d /etc/ipsec.d"

W trakcie wywołania powyższej komendy zostaniemy poproszeni o hasło ROOT-a, a następnie o hasło które wprowadziliśmy przy eksporcie. Jeżeli wszystko uda ło się zrobić tak jak trzeba powinien być na terminalu wpis pk12util: PKCS12 IMPORT SUCCESSFUL.

Super udało nam się zaimportować do bazy zawartość pliku z danymi PKCS#12 - przynajmniej wszystko na to wskazuje, ale sprawdźmy. Możemy to zrobić wykorzystując komendę:
su -c "certutil -L -d /etc/ipsec.d"

No dobrze wszystko gra w takim razie przechodzimy do konfiguracji IPSEC. Po pierwsze edytujemy plik /etc/ipsec/ipsec.secrets poprzez dodanie w nim następującej linii:
: RSA "masl-12" "[hasło]"

gdzie "masl-12" oznacza nazwę klucza wprowadzoną przy eksporcie przy użyciu 'openssl', a w miejsce [hasło] należy wpisać hasło w naszym przypadku jest to pusta wartość. Zapisujemy wyedytowany plik.

Co dalej. Dalej przechodzimy do edycji pliku /etc/ipsec/ipsec.conf i tutaj usuwamy to co jest już tam wpisane i zaczyna się od 'config setup' a następnie wpisujemy to co poniżej:
config setup
nat_traversal=yes
interfaces=%defaultroute
protostack=netkey
#plutodebug=all
#plutostderrlog=/tmp/pluto.log

conn IPSEC-conn
authby=rsasig
pfs=no
rekey=yes
keyingtries=3
type=transport
left=0.0.0.0 ; <- tutaj wpisać lokalne IP (ifconfig)
leftid="C=PL, ST=Malopolska, L=Cracow, O=BleBle, CN=Marcin Slowik, E=masl@xxxx.pl"
leftcert="masl-12" ; <- nazwa certyfikatu wprowadzona w kroku z komendą openssl
leftrsasigkey=%cert
leftprotoport=17/1701
right=
rightrsasigkey=%cert
rightca=%same
rightid=@xx.yyyyy.zz
rightprotoport=17/1701
auto=add

Bardzo ważną sprawą jest podanie jako 'leftid' identyfikatora w formie DN (distinguished name) - gdy tego się nie poda przedstawimy się serwerowi w formie adresu IP i możemy mieć problem 'no RSA no RSA public key known for 0.0.0.0' na serwerze a na kliencie INVALID_KEY_INFORMATION. Pytanie skąd wziąć tą informację - odpowiedź - wykonać:
su -c "certutil -L -d /etc/ipsec.d -n masl-12"

i skopiować wartość z pola 'Subject:'. Ważne jest to że w przypadku, gdy po wykonaniu komedy 'ipsec auto --up IPSEC-conn' (opisane w dalszej części) dostaniemy błąd 'INVALID_KEY_INFORMATION' należy skopiowane wartości odwrócić, czyli przekształcić z formy
E=masl@xxxx.pl,CN=Marcin Slowik,O=BleBle,L=Cracow,ST=Malopolska,C=PL
na formę
C=PL, ST=Malopolska, L=Cracow, O=BleBle, CN=Marcin Slowik, E=masl@xxxx.pl
. Jako 'left' należy podać lokalny adres sieciowy maszyny z której się łączymy, czyli jeżeli jesteśmy w sieci lokalnej np. 192.168.1.2. Można uzyć zamiast adresu IP wartości '%defaultroute' jednak polecam wartość IP na początek i jak wszystko zacznie działać to wprowadzenie '%defaultroute' ponieważ może to być przyczyną niepotrzebnych poszukiwań co jest nie tak. Jako 'right' należy podać adres IP lub nazwę hosta docelowego / serwera - polecam przy pierwszej próbie wpisać IP potem ewentualnie nazwę (tutaj też czasem są problemy których przy pierwszym podejściu proponuję unikać). Jako 'rightid' należy wpisać po małpce FQDN (Fully Qualified Domain Name) serwera.

No dobra zróbmy pierwszą próbę czy wszystko działa w miarę dobrze. Zróbmy następującą sekwencję komend:

su
service ipsec stop
service ipsec start

Po takiej sekwencji powinniśmy dostać po wykonaniu:
tail -f /var/log/secure

mniej więcej coś takiego na końcu:
Feb 16 22:34:00 fedora pluto[3874]: loaded private key for keyid: PPK_RSA:DwBnRRRR

Ważne jest jeszcze, żeby sprawdzić czy zostało dodane połączenie o nazwie 'IPSEC-conn', czyli sprawdzenie czy w pliku '/var/log/secure' znajduje się coś w tym stylu:
Feb 16 22:34:00 fedora pluto[3874]: added connection description "IPSEC-conn"

Teraz sprawdzamy poprawność połączenia i wymiany kluczy, czyli wykonujemy z linii komend:
ipsec auto --up IPSEC-conn


Jeżeli wszystko działa poprawnie powinniśmy dostać na konsoli coś w tym stylu:
117 "IPSEC-conn" #2: STATE_QUICK_I1: initiate
004 "IPSEC-conn" #2: STATE_QUICK_I2: sent QI2, IPsec SA established transport mode {ESP=>............}

No dobra jeżeli działa to przechodzimy do następnego kroku, czyli konfiguracji xl2tp. Konfiguracja xl2tp polega na modyfikacji trzech plików. Pierwszy to /etc/ppp/chap-secrets. W tym pliku dodajemy następującą linię:

test * test *

pierwsze 'test' to user-name drugie 'test' to hasło, czyli informacje potrzebne do podstawowej autentykacji połączenia z serwerem.

Następny plik to /etc/ppp/options.l2tpd.con. Ten plik tworzymy i wypełniamy następującą treścią:

ipcp-accept-local
ipcp-accept-remote
refuse-eap
noccp
noauth
crtscts
idle 1800
mtu 1410
mru 1410
nodefaultroute
debug
lock
connect-delay 5000

No i ostatni z plików to /etc/xl2tpd/xl2tpd.conf, w którym dodajemy to co następuje:

[global]
listen-addr = 192.168.1.2

[lac L2TPS]
lns = [FQDN for serwer]
require chap = yes
refuse pap = yes
require authentication = yes
name = test
ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd.con
length bit = yes


No dobra to w takim razie odpalamy xl2tpd:
service xl2tpd start

Jeżeli wszystko ok powinniśmy mieć w pliku /var/log/messages wpis:


Feb 16 23:29:06 fedora xl2tpd[4955]: Listening on IP address 192.168.1.2, port 1701

No dobra teraz odpalmy połączenie, czyli do wykonania są następujące komendy:

ipsec auto --up IPSEC-conn
echo "c L2TPS" > /var/run/xl2tpd/l2tp-control


Jeżeli po wykonaniu komend powyżej dostałeś w pliku /var/log/messages coś takiego:
Feb 17 00:40:31 fedora pppd[9709]: Warning: can't open options file /root/.ppprc: Permission denied

to proponuję przejść do ustawień polityki selinux raczej na pewno nie są to inne uprawnienia.

No dobrze dalej jak mamy połączenie to musimy zdefiniować routing dla adresów z podsieci na serwerze, ponieważ domyślnie mamy dostępny jeden adres. Można to zrobić np. dla całej puli adresów sieci prywatnej zaczynających się od 10. w sposób podany niżej:

ip route add 10.0.0.0/8 dev ppp0


Można jeszcze na przykład dodać name server, który znajduje się w sieci prywatnej na adresie 10.5.1.1 w sposób podany poniżej:

echo "nameserver 10.5.1.1" >> /etc/resolv.conf


Zamykanie wszystkiego po kolei to wykonanie skryptu:

ip route del 10.0.0.0
echo "d L2TPS" > /var/run/xl2tpd/l2tp-control
ipsec auto --down IPSEC-conn
service xl2tpd stop
service ipsec stop


To wszystko na ten temat. Mam nadzieję, że to komuś pomoże w konfiguracji połączenia VPN.

GlossyBlue Blogger by Black Quanta. Theme & Icons by N.Design Studio
Entries RSS Comments RSS