Optimalizace obsluhy Sér.portu - brzdí zbytek programu – Delphi – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Optimalizace obsluhy Sér.portu - brzdí zbytek programu – Delphi – Fórum – Programujte.comOptimalizace obsluhy Sér.portu - brzdí zbytek programu – Delphi – Fórum – Programujte.com

 

Toto vlákno bylo označeno za vyřešené.
Vlákno bylo úspěšně vloženo.
Pokud sám přijdeš na řešení, nezapomeň ho sem přidat!
MilanL+1
Grafoman
25. 4. 2017   #1
-
0
-

Ahoj, potřeboval bych poradit jak vyřešit Brzdu při čekání na odpověď ze sériového portu.
Na obsluhu COM používám knihovnu Synaser,

Struktura: MainForm   se SubFormuláři:
                                                   Form Obsluha měř.přístroje obsahuje i komunikaci a vyčítání dat 
                                                   Form Detail měření s ručním doplňováním údajů k měření
                                                   Form s dbgridem s přehledem historie měření

Komunikace funguje obousměrně: odeslání Příkazu > přijem Odpověďi/í, každý příkaz má odpověď.
Současné řešení: smyčka max.počet odpovědí se Synaser.ReceiveString s timeoutem 500 nebo 1000, ošetření na 5 Timeoutů (=chyba komunikace) a existenci odpovědi, na konci smyčky je momentálně sleep(250) - pauza před dalším Receive.  V okamžiku, kdy běží na přístroji měření je odezva pomalejší - tím pádem smyčka probíhá několikrát dle nastavení timeoutů u receive a počtu do chyby x Sleep, což výrazně brzdí odezvu v ostatních Subformech. Ostatní komunikace to tolik nebrzdí - na startu při inicializaci sériově několik příkazů/odpovědí zdržení neřeším (aspoň neutíká zobrazení statusu), stav měř.př. řeším přes timer v intervalech 5-10s. 

Moje nápady na optimalizaci co nejmenší brzdy u čekání na odpověď, potřebuji poradit, který vybrat, případně návrh jiného řešení:
- nahradit Sleep prasárničkou Delay se smyčkou a App.ProcesMessages
- prodloužit Timeout u volání Synaser.ReceiveString (nevím jak funguje zda při příchodu odpovědi skončí hned nebo čeká po celý TOut)
- přesun Komunikace portu do Threadu, zde nevím, jak řešit čekání na konec jestli zas použít "Delay", nebo zda na to je nějaká funkce, případně řešení odpovědí přes backend proceduru.

Za rady a návrhy předem Děkuji

Nahlásit jako SPAM
IP: 91.139.9.–
25. 4. 2017   #2
-
0
-

Obvykle se to řeší Threadem. Pro "uspání" Threadu jsem používal funkci WaitForSingleObject a pro ukončení čekání Event objekt.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
MilanL+1
Grafoman
25. 4. 2017   #3
-
0
-

#2 hlucheucho

Myslíš dát do Threadu celej form, nebo jeho obsluhu?

Na to jsem taky myslel, ale co jsem našel na netu, tak všude píšou, že Form v Threadu je unsafe, proto jsem se tomu zatím bránil.

Pokud bych dal do threadu jen komunikaci, stále bych ve formuláři musel řešit čekání na výsledek s pozicí odkud tu komunikaci volám.

Nahlásit jako SPAM
IP: 91.139.9.–
MilanL+1
Grafoman
25. 4. 2017   #4
-
0
-

sory za Bold.

Nahlásit jako SPAM
IP: 91.139.9.–
25. 4. 2017   #5
-
0
-

Form a další komponenty VCL musí běžet v hlavním Threadu. Samotnou komunikaci nebo jen to čekání dáš do Threadu. Po přijetí dat nebo vypršení timeoutu z Threadu zavoláš uvnitř Synchronize metodu formuláře, která převezme přijatá data. Kromě toho lze i hlavnímu oknu poslat zprávu pomocí funkce SendMessage. Třetí způsob (asi nejméně vhodný) je použití globálních proměnných a synchronizace např. pomocí CriticalSection

hu

Nahlásit jako SPAM
IP: 195.178.67.–
MilanL+1
Grafoman
25. 4. 2017   #6
-
0
-

#5 hlucheucho
90% toho co navrhuješ já chápu.

Co nechápu je, jak mám v lineárním programu, kde mám za sebou několikrát volání komunikace a reakci na odpověď na tu odpověď čekat, tak aby mi to nebrzdilo zbytek programu,

Pro představu hodím kousek kodu z inicializace formu: 

  LgPrg.Line(P, clBlack, 'Test komuninace');     // logování
  PortWR(X);	           // Komunikace odeslání příkaz X a čekání na odpověď  
  if Odpovedi.Count=0 then OldData; // Otestuj zda v bufferu nezůstala vyčítaná data
  PortWR(H);               // Komunikace odeslání příkaz H-elp a čekání na odpověď
  if Odpovedi.Count=0 then
  begin
    LgPrg.LineBarva(clRed);
    LgPrg.Line(P, clRed, ' ... CHYBA');
    LgPrg.Line(P, clRed, ' Zkontrolujte nastavení PORTU v konfiguraci. ');
    SPort.CloseSocket;
    exit;
  end;
  LgPrg.LineBarva(clGreen);
  LgPrg.Line(P, clGreen, ' ... OK');

  LgPrg.Line(P,clBlack, 'Vyčtení nastavení DMA');
  LgPrg.Line(P,clBlack, '   - ... Metoda');
  PortWR(GMN);               // Komunikace příkaz GetMethodName a čekání na odpověď
  if not TSLHledej(Odpovedi,aKontr[ord(GMN)],I) then
  begin
    LgPrg.LineBarva(clRed);
    LgPrg.Line(P,clRed, '  -  CHYBA při zjišťování Metody' );
    SPort.CloseSocket;
    exit;
  end;
  LgPrg.LineBarva(clGreen);
  ArStr := textparser(',',textparser(':',Odpovedi[I])[1]);
  lMetoda.Caption :=
            ansiuppercase(ArStr[0]);
  Status.DMA.Metoda := StrToInt(ArStr[1]);
  LgPrg.Line(P,clBlack, '   - ... Data Head');
  PortWR(GDH);               // Komunikace příkaz GetDataHead a čekání na odpověď
  if not TSLHledej(Odpovedi,aKontr[Ord(GDH)],I) then
  begin
    LgPrg.LineBarva(clRed);
    LgPrg.Line(P,clRed, '  -  CHYBA při zjišťování Hlavičky dat');
    SPort.CloseSocket;
    exit;
  end;
Nahlásit jako SPAM
IP: 91.139.9.–
MilanL+1
Grafoman
25. 4. 2017   #7
-
0
-

#5 hlucheucho
Nejspíš nejdřív otestuji prodloužení Timeoutu u voláni ReceiveString a vynechání toho Sleepu.

Ta komunikace v threadu, tam bych to musel řešit přes globální statusy a TStrings s odpověďmi, ale stále by tam bylo třeba řešit buď čekání nebo v threadu obsluhu component (obsahy labelů) formu.

Nahlásit jako SPAM
IP: 91.139.9.–
25. 4. 2017   #8
-
0
-

Obsluhu Labelů z Threadu je snadná. Přímo v šabloně Threadu je ukázka jak na to. Představoval bych si to asi takto:

1. Vytvořím Thread, který zahájí vysílání a pak čeká na příjem
2.Thread opakovaně testuje příjem a timeout (smyčka která proběhne např každých 10ms)
3a. Pokud nastal příjem, Thread přijatá data zkompletuje a zavolá metodu hlavního formuláře přebírající přijatá data (použije se Synchronize)
3b. pokud vypršel timeout, Thread zavolá metodu hlavního formuláře pro chybu (použije se Synchronize)
4. Thread se ukončí
5. mrtvý Thread uvolnit z paměti aby nevznikl Memory Leak

Druhá možnost je nepřetržitě běžící Thread jako stavový automat, který si testuje, zda má něco odesílat a po každé odeslané zprávě provádí body 1 - 3. Pak je třeba zajistit ukončení Threadu při ukončování aplikace, pro inspiraci http://stackoverflow.com/questions/4044855/how-to-kill-a-thread-in-delphi "Zprávy" k odeslání lze řadit do fronty a Thread si bude z fronty odebírat, při přístupu k této frontě je nutná synchronizace např. pomocí Critical Section, jde to udělat i jako volání metody formuláře s použitím Synchronize. Thread může i párovat odesílanou zprávu s odezvou a tento pár vracet hlavnímu formuláři v metodách popsaných výše v bodech 3a, 3b.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
MilanL+1
Grafoman
25. 4. 2017   #9
-
0
-

#8 hlucheucho
Ano tohle chápu, otázkou je jak sledovat odkud je ta komunikace volána a jaký příkaz, musel bych to nějak nastavovat, aby program u toho zpětného volání věděl, kde má pokračovat - nejspíš udělat nějakej rozcestník.

Nahlásit jako SPAM
IP: 91.139.9.–
MilanL+1
Grafoman
25. 4. 2017   #10
-
0
-

#9 MilanL
start MainFormu:
- načtení konfigurace
- Konf.Měřák připojen přidání Formu měřáku do PageControlu
   - Inicializace formu Měřáku (včetně Timeru, portu a vyčtení nastavení Měřícího přístroje)
   - po inicializaci už je zbytek Formu měřáku řízen Timerem a Eventy, ale ty funkce vždy jako celek od odeslání komunikace po zpracování odpovědí (to bych musel rozdělit a definovat v rozcestníku v návratové proceduře pro Thread)

- Konf.DB - Nastavení parametrů spojení DB a Přidání Subformů pro DB

P.S.: ještě drobnost děláš i DB, přemýšlím, jak bych vyřešil v rámci daného programu možnost různých typů DB (MSSQL, MySQL, ORA, MS ACCESS) , zatím mám naprogramovaný datamodul pro MSSQL přes ADO, jde o rozdíly mezi SQL scripty, přemýšlel jsem nad dynamickými balíčky bud s celým data modulem nebo aspon se stringy scriptů.

Nahlásit jako SPAM
IP: 91.139.9.–
25. 4. 2017   #11
-
0
-

Thread může vybrat podle situace kterou metodu hlavního formuláře zavolá, uvnitř této metody vykonáš, co je třeba, např zapíšeš do labelu okamžitou hodnotu nebo stav. Na straně hlavního formuláře se to bude chovat jako událostmi řízené programování, nemusí na nic čekat. Lze zajít i tak daleko, že Thread přečte (ze souboru?) konfiguraci, otevře si COM, nastaví periferii a sám zahájí pravidelné vyčítání hodnoty a tu pak předává hlavnímu formuláři. Pokud platí 1 záložka = 1 měřák = 1 COM, pak může platit, že každá záložka má svůj Thread který se o práci s měřákem sám stará. Pokud je měřák a/nebo COM sdílený, je vhodnější strategie jeden Thread s frontou na vstupu, kam se vloží zpráva spolu s identifikací jejího původce a Thread pak tuto informaci použije pro určení příjemce dat. Identifikátorem původce může být např ukazatel na záložku a pak lze pomocí tohoto ukazatele určit, do které záložky přijatá data vypsat. Pak stačí aby si Thread pamatoval tuto informaci od převzetí zprávy z vysílací fronty a zahájení vysílání až po přijetí odpovědi nebo vypršení timeoutu a předání odezvy.

Řízení vysílaní Timerem může fungovat tak, že na "tiknutí" Timeru se do fronty vloží "zpráva" k odeslání a ověří se délka vysílací fronty. Pokud fronta neúměrně narůstá, znamená to chybu.

Z databází jsem dělal jen s MySQL. Myslím, že ADO by mohla být dobrá volba. Akorát... SQL je tak trochu "nestandartní standart" a tak narazíš odchylky mezi jednotlivými DB. Silně se u Delphi propaguje FireDAC, osobně jsem s ním udělal špatnou zkušenost. Chyba v něm byla pro mne poslední kapkou a tak jsem C++ Builder opustil, skončil jsem u verze XE5. U verze XE6 byla tato chyba údajně opravena.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
peter
~ Anonymní uživatel
3981 příspěvků
26. 4. 2017   #12
-
0
-

Nezkoumal jsem, jak to presne funguje v delphi, thredy a pod, ale obecne je to reseni pres casovace. Neco, jako mas ve windows Planovac uloh, Procesy.

Priklad 1: Mas treba funkci 'zobraz kurzor mysi'. Tato funkce se opakovane spousti treba kazdych 20 ms. Funkce precte data z portu, zobrazi sipecku na x,y, prepise ve vnejsim objektu mysi souradnice a prida i informaci o tom, jake tlacitko jsi zmackl. Pokud pak spustis nejaky program, ktery zjistuje polohu mysi, tak se pta toho vnejsiho objektu.

Priklad 2: Webova stranka, ajax nebo nacitani obrazku (asynchronni prenos)
Tam je program, co si vykresluje stranku. Spusti treba 5 casovacu pro stahovani obrazku. A ty maji callback funkci, ktera se spusti, az stahovani skonci. Cili to funguje uplne stejne jako priklad 1 s tou mysi. 

prenos = {}
prenos.data = ''
prenos.callback = null
prenos.start = function (url, callback) {
  prenos.callback = callback; 
  prenos.transferStart(url);
  }
prenos.stop  = function () {prenos.callback(prenos.data)}
prenos.transferStart = function () {inicializace...}
prenos.transferRun   = function (newdata) {prenos.data += newdata; if (newdata==end_znak) {prenos.transferStop()}}
prenos.transferStop  = function () {zastaveni... prenos.stop()}

function zobraz(data) {alert(data);} // alert('ahoj')
prenos.start('https://seznam.cz', zobraz);

// start: callback - je pointer na funkci
// stop: spusteni funkce prenos.callback(prenos.data)
// stop: Tez se to v javascriptu pise jako volani funkce z globalniho 
//   objektu/pole window, window[prenos.callback](prenos.data)
//   Jakoze vytvaris vsechny promenne do window
//   window.prenos = {}
//   window.prenos.callback = ...
// alert: js funkce, ktera zobrazi okenko typu 'alert' s textem

Spoustis to v programu prikazem
To zaradi do systemoveho casovace prikaz
prenos.transferRun(), rekneme s opakovanim 20ms a kontrolou stavu, zda uz sam nebezi
Kdybys nekontroloval stav, tak ti tam treba vytvori, nez ta funkce dobehne, dalsich 50 spusteni, treba.
Kazdych 20 ms se pak pokusi zkontaktovat server a pozadat ho o dalsi cas stranky seznam.cz. Kdyz se to nezdari, tak proste ni, zkusi to priste. Tohle dela, dokud prenos neskonci nebo mu to nevykrizkujes (treba tlacitkem: zastavit stahovani). Podobne jako ten kurzor mysi, ten zobrazuje stale.

udelej predtim...
prenos...
udelej potom...
ostatni program...


function fcePotom(prijata_data) {udelej potom...}
udelej predtim...
fcePrenos(fcePotom);
ostatni program...

To jen, jak to funguje principialne...

Nahlásit jako SPAM
IP: 2001:718:2601:26c:b0ce:d6...–
MilanL+1
Grafoman
26. 4. 2017   #13
-
0
-

#11 hlucheucho
Díky moc,
za pomoc.

Než to překopnu do vlákna (zatím jsem s vlákny nepracoval, takže mi to chvilku potrvá než to všechno správně naprogramuji - vlákno, zpětné eventy atd.) , tak jsem to dočasně vyřešil tou Delay smyčkou s ProcesMessages a trošku jsem poupravil tu komunikaci, zvýšil jsem počet timeoutů na chybu, zkrátil jsem Timeouty na 1/3 (100-200ms) a v každém cyklu je Delay Pauza 1:1 s timeoutem čtení. Otestoval jsem to a jede to v pohodě bez drbání v práci s ostatními částmi, ani se neobjevilo žádný extra zatížení CPU tím delayem jak píšou na netu, ikdyž zatím to testuju na i5 PC, otázkou je jak to pojede v nasazení na EmbedXP panelu. 

Nahlásit jako SPAM
IP: 91.139.9.–
MilanL+1
Grafoman
26. 4. 2017   #14
-
0
-

#12 peter
o to nejde principiálně bych s tím problém neměl u jednotlivých činností, jak to píšeš, jen bych musel tu lineární část děsně rozkouskovat např. v inicializaci mám asi 6x komunikaci s potřebou odpovědi před dalším pokračováním (vyčisti buffer, zjisti měřící metodu, zjisti formát datové věty, zjisti měrné jednotky pro jednotlivé údaje datové věty, zjisti status zda [ne]běží měření), pokud něco neproběhne vrací se do Mainu chyba, proběhne v pořádku končí základní programová inicializace  a zbytek už jde přes timer a události podle flagů.

Komunikace měřáku je pomalá mezi příkazem a odpovědí podle situace je cca 250ms- cca 3-4s (ten dlouhej čas bejvá, když skončí měření a měřák je vytíženej uložením dat do FIFO a tiskem štítku).

Nahlásit jako SPAM
IP: 91.139.9.–
MilanL+1
Grafoman
26. 4. 2017   #15
-
0
-

#14 MilanL
rozkouskování by vypadalo asi takhle: 

start inicializace
{  .... přenos(vyčisti buffer, [callback] Clearbuf)}
Clearbuf
{  obsluha po přenosu....přenos(zjisti metodu, [callback] Metoda}
Metoda
{  po přenosu ... přenos(zjisti hlavičku dat, [callback] datahead}
... další přenosy v inicializaci a na konci nastavení flagů pro timer
v případě nezdaru callback nebo sendmessage do mainu.
Nahlásit jako SPAM
IP: 91.139.9.–
MilanL+1
Grafoman
26. 4. 2017   #16
-
0
-

#15 MilanL
nebo by to mohlo být v jednom callbacku  a přes proměnnou dělat větvení CaseOf na obsluhu a odeslání dalšího příkazu, jako řešení vidím víc. jen vybrat optimální.

Kde jsou ty doby, kdy jsem dělal na IQ151 plovoucí texty přes přerušení ve strojáku, programuji občasně přes 30 let, jen je tohle poprvý co dělám se sériovou komunikací a složitěji organizovanej appForm, dřív to byly spíš DB v Accessu nebo nějaký malý Excelový kódy (mimochodem jsem tu komunikaci nejdřív zkoušel i v Excelu, ale nějak mi to zamrzalo).

Nahlásit jako SPAM
IP: 91.139.9.–
26. 4. 2017   #17
-
0
-

Ještě jsem si uvědomil drobný detail. Příjemce dat nemusí být shodný s odesílatelem a tak do fronty můžeš vkládat ukazatele na objekt, který převezme nejen data, ale i řízení. Pro takový případ je vhodné mít třídu s implementací obecného chování s virtuální metodou pro přijetí dat a u potomků tuto metodu překrýt a implementovat tak konkrétní chování. Pro každou akci můžeš mít samostatný objekt, něco na způsob štafety. Stejně tak lze v jednom objektu vytvořit stavovou logiku pro inicializaci portu a měřáku a pro vyčítání naměřené hodnoty předat řízení jinému objektu. Takto odpadá nutnost použít rozsáhlý switch. Tyto objekty nečekají, pouze existují a konají akci až když je volána jejich metoda , což provádí Thread. Šikovné rozebrání na dílčí problémy a tomu odpovídající rozdělení do objektů usnadní práci. Další výhodou objektu je snadné předání dat, metody volané prostřednictvím Synchronize nelze volat s parametry. Nastuduj si polymorfismus.

Další řešení je do fronty vkládat ukazatel na funkci, která data zpracuje. Podle toho, na kterou funkci dáš ukazatel, se pak program bude chovat. Toto řešení ti předložil peter. Tento neobjektový koncept je hůře čitelný. 

Co se týče psaní Threadu. Delphi má pro to šablonu, do projektu se vloží Thread Object. Je to v podstatě třída jako každá jiná. Pokud Threadu chceš předat nějaké parametry (název portu, cestu k souboru s konfigurací ....), můžeš si vytvořit parametrický konstruktor. V metodě Execute pak implementuješ, co má Thread dělat. Je tam na to nápověda Place your code here a také je tam nápověda, jak volat metody hlavního formuláře. Pokud má Thread běžet neustále, musíš použít cyklus, odkaz jsem ti výše uvedl. Instanci Threadu vytváříš dynamicky stejně jako u každé jiné třídy zavoláním konstruktoru.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
MilanL+1
Grafoman
27. 4. 2017   #18
-
0
-

#17 hlucheucho
Jo v pohodě já to chápu, šlo hlavně o to vyřešit situaci, kdy program potřebuje počkat na odpověď, aby mohl pokračovat (ta série příkazů a odpovědí v inicializaci), po této sérii už se to může řešit threadem s callbacky nebo Timerem s ošetřením stavů, frontu určitě řešit nebudu, bo ty odpovědi s daty potřebuji ručně doplňovat.

Stejně to časem budu muset nějak vyřešit, s jedním ze 2 měřáků co tu máme jsou nějaký problémy, tak možná budou kupovat novější typ s USB nebo Síťovým rozhraním a to bude, pak ještě zajímavé.
Zajímavý je proč výrobce nedodává nějakou DB aplikaci s měřákem, jediný co jsme sehnali byl plugin do excelu na vyčítání do tabulky, které by blokovalo tisk štítků, při jejich ceně (několik set tisíc) by už člověk očekával něco lepšího.

Kdyby se uvažovalo o napojení obou měřáků, každý bude buď na samostatném portu (takže v pohodě, každý vlastní Objekt ať už Form nebo Thread), nebo jiný PC (podle vzdálenosti od sebe).

Nahlásit jako SPAM
IP: 91.139.9.–
MilanL+1
Grafoman
27. 4. 2017   #19
-
0
-

#18 MilanL
ještě taková drobnost proč řeším to čekání na inicializaci, chtěl jsem ušetřit výkon a paměť toho staršího embedXP panelu, tak mám některé Objekty (formuláře) dynamicky, nevěděl jsem předem kolik paměti to bude žrát vzhledem k práci s DB. 

No zatím to vypadá, že jsem to zkoumal a dělal zbytečně, základ programu zatím běží v cca 6-7MB RAM o další 2-10M to narůstá, při práci s DB hlavně pak při generování rozsáhlejších výstupních sestav.

Nahlásit jako SPAM
IP: 91.139.9.–
27. 4. 2017   #20
-
0
-

Měřáky do USB často tvoří virtuální COM. U síťového se dají použít sokety. Ve specifikaci přístroje by měly být specifikace jak komunikovat. Pokud má být měřák vyměněný za jiný, budou asi i rozdíly v komunikaci s ním. Pak je objektový koncept šikovnější, při dobrém návrhu ho nebude problém přizpůsobit jinému měřáku a možná i práci po síti. "Comový" Thread vyměníš za Thread "síťový" a princip zůstane stejný.

Proč výrobci nedělají DB aplikace je snadné vysvětlit. Měřáky se používají jako součást složitějších systémů a udělat SW tak univerzální aby to šlo integrovat je problém. Proto se dodávají komunikační protokoly a spoléhá se na to, že bude SW naprogramovaný na zakázku pro konkrétní účel. SW od výrobce měřáku je spíš takový demo.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
MilanL+1
Grafoman
27. 4. 2017   #21
-
0
-

#20 hlucheucho
no pro mě je teď důležité, že už to pracuje jak má a nebrzdí to viz #13.
Teď ještě dělám na reportech, chtěl jsem původně použít dodávanou plugin RaveReport, ale nemá to ty možnosti co potřebuji, tak jsem najel na PrintPreview a přenos a formátování dat na report si musím dodělat sám. A až to bude hotový a pošlape to, bude čas na refaktoring v rámci kterého překopnu tu komunikaci. Chtěl bych ten program víc zuniverzálnit, jak třeba možnost různých typů měřáků (různé příkazy a odpovědi), různé komunikace (COM/NET a USB pokud bude třeba řešit) , tak třeba pořešit i ty různé DB (MSSQL/MySQL...), ale vše postupně. 

Nahlásit jako SPAM
IP: 91.139.9.–
MilanL+1
Grafoman
27. 4. 2017   #22
-
0
-

#20 hlucheucho
u těch threadů COM / síť to bude asi dobrý udělat přes společnýho abstract předka co? abych do objektu mohl přidat, kterýkoliv z nich a nemusel v programu řešit nikde jinde.

Nahlásit jako SPAM
IP: 91.139.9.–
27. 4. 2017   #23
-
0
-

#22 MilanL
Myslím, že ano

hu

Nahlásit jako SPAM
IP: 195.178.67.–
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 4 hosté

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý