بایگانی برچسب برای: n tdgjva;k

راه اندازی کانال شرکت بهسان اندیش در تلگرام و سایر پیام رسان ها

کانال شرکت بهسان اندیش در تلگرام و سایر پیام رسان ها– در دنیای مجازی که شبکه های اجتماعی همچون کشوری مستقل عمل می کنند دسته ای از کاربران متناسب با فعالیت خود نیاز به امکاناتی خاص تر دارند. سازمان های دولتی و خصوصی، برند ها، شخصیت های سیاسی، هنری، فرهنگی و… برای ایجاد ارتباط و پیشبرد فعالیت خود نیاز به اعتماد مخاطب دارند.

در شبکه های اجتماعی ، گاه هویت واقعی یک کاربر مشخص نیست و ممکن است دیگران به اسم سازمان ها و اشخاص صفحاتی را ایجاد کنند و نام آن برند یا شخص را خدشه دار نمایند و با توجه به آنکه ایجاد اعتماد در مخاطب، امری مشکل است ، تلگرام با رسمیت بخشیدن به صفحات و تایید آنها این رویکرد را متفاوت کرده است.

شرکت بهسان اندیش به منظور ارائه خدمات و فعالیت های خود در شبکه های اجتماعی اقدام به راه اندازی کانال شرکت بهسان اندیش در تلگرام و پیام رسان های داخلی نموده که علاقمندان می توانند از طریق لینک زیر در این پیام رسان ها ما را دنبال کنید:

? پیام رسان تلگرام (مرکز اصلی اطلاع رسانی شرکت) :

https://t.me/Behsan_Transit

 

? در سروش (با اینترنت نیم بها) :
sapp.ir/behsanandish

 

 

 

کار با Thread ها در زبان سی شارپ :: آشنایی با مشکل Concurrency در برنامه های Multi-Threaded و راهکار های رفع این مشکل

زمانی که ما برنامه های Multi-Threaded می نویسیم، برخی اوقات Thread های ایجاد شده به داده های مشترک در سطح برنامه دسترسی دارند و وظیفه ما به عنوان برنامه نویس این است که مطمئن باشیم دسترسی چند Thread به داده های مشترک باعث بروز مشکل نمی شود. برای آشنایی بیشتر با این موضوع شرایطی را در نظر بگیرید که یک متد قرار است در چندین thread مختلف به صورت جداگانه اجرا شود، بعد از شروع کار هر thread زمانبندی اجرا توسط CLR به هر thread به صورت خودکار انجام شده و ما نمی توانیم دخالتی در این موضوع داشته باشیم، ممکن است در این بین اختصاص زمان به یک thread بیش از thread دیگر انجام شود و در این بین خروجی مناسب مد نظر ما ایجاد نمی شود. برای آشنایی با این موضوع متد PrintNumbers که در زیر تعریف کردیم را در نظر بگیرید:

public static void PrintNumbers()
{
    Console.Write("{0} is printing numbers  >  " , Thread.CurrentThread.Name);
    for (int counter = 0; counter  <  10 ;  counter++)
    {
        Thread.Sleep(200*new Random().Next(5));
        Console.Write("{0},", counter);
    }
    Console.WriteLine();
}

در مرحله بعد متد Main را به صورت زیر تغییر می دهیم تا 10 thread ایجاد شده و سپس کلیه thread ها اجرا شوند:

Thread[] threads = new Thread[10];

for (int index = 0; index  <  10 ;  index++)
{
    threads[index] = new Thread(PrintNumbers);
    threads[index].Name = string.Format("Worker thread #{0}.", index);
}

foreach (var thread in threads)
{
    thread.Start();
}

Console.ReadLine();

همانطور که مشاهده می کنید کلیه thread ها به صورت همزمان اجرا می شوند، اما پس از اجرا کد بالا، خروجی برای بار اول به صورت خواهد بود، البته دقت کنید که با هر بار اجرا خروجی تغییر می کند و ممکن است برای بار اول خروجی زیر برای شما تولید نشود:

Worker thread #0. is printing numbers  >  Worker thread #1. is printing numbers  >  Worker thread #2. is printing numbers  >  Worker thread #3. is printing numbers  >  0,Worker thread #4. is printing numbers  >  0,1,1,2,3,2,Worker thread #5. is printing numbers  >  0,4,3,1,4,2,5,Worker thread #6. is printing numbers  >  5,3,Worker thread #7. is printing numbers  >  4,6,6,0,Worker thread #8. is printing numbers  >  Worker thread #9. is printing numbers  >  0,0,7,5,7,0,1,0,1,0,6,8,8,1,2,1,1,0,2,9,
7,3,2,2,9,
2,1,8,3,3,3,4,2,1,9,
4,4,4,5,3,3,5,5,5,6,2,6,6,7,6,4,4,7,7,8,9,
8,9,
7,8,9,
5,5,3,8,6,7,8,9,
6,7,8,9,
4,5,9,
6,7,8,9,

اگر برنامه را مجدد اجرا کنید خروجی متفاوتی از خروجی قبلی دریافت خواهیم کرد:

Worker thread #0. is printing numbers  >  Worker thread #1. is printing numbers  >  Worker thread #2. is printing numbers  >  Worker thread #3. is printing numbers  >  Worker thread #4. is printing numbers  >  Worker thread #5. is printing numbers  >  Worker thread #6. is printing numbers  >  0,Worker thread #7. is printing numbers  >  1,2,0,1,3,2,4,3,Worker thread #8. is printing numbers  >  Worker thread #9. is printing numbers  >  0,0,0,0,0,0,5,4,5,6,7,8,9,
6,7,8,9,
1,1,0,0,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,4,5,6,7,8,9,
3,3,3,3,3,4,4,4,4,5,6,7,8,9,
5,6,7,8,9,
5,6,7,5,6,7,8,9,
8,4,4,4,5,5,6,6,7,7,9,
5,8,8,6,9,
9,
7,8,9,

همانطور که مشاهده می کنید خروجی های ایجاد کاملاً با یکدیگر متفاوت هستند. مشخص است که در اینجا مشکلی وجود دارد و همانطور که در ابتدا گفتیم این مشکل وجود همزمانی یا Concurrency در زمان اجرای thread هاست. زمابندی CPU برای اجرای thread ها متفاوت است و با هر بار اجرا زمان های متفاوتی به thread ها برای اجرا تخصیص داده می شود. اما خوشبختانه مکانیزم های مختلفی برای رفع این مشکل و پیاده سازی Synchronizqation وجوددارد که در ادامه به بررسی راهکاری های مختلف برای حل مشکل همزمانی می پردازیم.

پیاده سازی Synchronization با کلمه کلیدی lock

اولین مکانیزم مدیریت همزمانی در زمان اجرای Thread ها استفاده از کلمه کلیدی lock است. این کلمه کلیدی به شما این اجازه را می دهد تا یک scope مشخص کنید که این scope باید به صورت synchronized بین thread ها به اشتراک گذاشته شود، یعنی زمانی که یک thread وارد scope ای شد که با کلمه کلیدی lock مشخص شده، thread های دیگر باید منتظر شوند تا thread جاری که در scope قرار دارد از آن خارج شود. برای استفاده از lock شما اصطلاحاً می بایست یک token را برای scope مشخص کنید که معمولاً این کار با ایجاد یک شئ از نوع object و مشخص کردن آن به عنوان token برای synchronization استفاده می شود. شیوه کلی استفاده از lock به صورت زیر است:

lock(token)
{
    // all code in this scope are thread-safe
}

اصطلاحاً می گویند کلیه کدهایی که در بدنه lock قرار دارند thread-safe هستند. برای اینکه کد داخل متد PrintNumbers به صورت thread-safe اجرا شود، ابتدا باید یک شئ برای استفاده به عنوان token در کلاس Program تعریف کنیم:

class Program
{
    public static object threadLock = new object();

    ....

در قدم بعدی کد داخل متد PrintNumbers را به صورت زیر تغییر می دهیم:

public static void PrintNumbers()
{
    lock (threadLock)
    {
        Console.Write("{0} is printing numbers  >  " , Thread.CurrentThread.Name);
        for (int counter = 0; counter  <  10 ; counter++)
        {
            Thread.Sleep(200 * new Random().Next(5));
            Console.Write("{0},", counter);
        }
        Console.WriteLine();
    }
}

با اعمال تغییر بالا، زمانی که thread جدیدی قصد وارد شدن به scope مشخص شده را داشته باشد، باید منتظر بماند تا کار thread جاری به اتمام برسد تا اجرای thread جدید شروع شود. با اعمال تغییر بالا، هر چند بار که کد نوشته شده را اجرا کنید خروجی زیر را دریافت خواهید کرد:

Worker thread #0. is printing numbers  >  0,1,2,3,4,5,6,7,8,9,
Worker thread #1. is printing numbers  >  0,1,2,3,4,5,6,7,8,9,
Worker thread #2. is printing numbers  >  0,1,2,3,4,5,6,7,8,9,
Worker thread #3. is printing numbers  >  0,1,2,3,4,5,6,7,8,9,
Worker thread #4. is printing numbers  >  0,1,2,3,4,5,6,7,8,9,
Worker thread #5. is printing numbers  >  0,1,2,3,4,5,6,7,8,9,
Worker thread #6. is printing numbers  >  0,1,2,3,4,5,6,7,8,9,
Worker thread #7. is printing numbers  >  0,1,2,3,4,5,6,7,8,9,
Worker thread #8. is printing numbers  >  0,1,2,3,4,5,6,7,8,9,
Worker thread #9. is printing numbers  >  0,1,2,3,4,5,6,7,8,9,

پیاده سازی Synchronization بوسیله کلاس Monitor

در قسمت قبل که از کلمه کلیدی lock استفاده کردیم، در حقیقت به در پشت زمینه از کلاس Monitor که در فضای نام System.Threading قرار دارد استفاده شده است. زمانی که از کلمه کلیدی lock استفاده می کنیم این کد تبدیل به کدی می شود که از Monitor برای پیاده سازی Synchronization استفاده می کند (می توان این موضوع را با ابزار ildasm.exe و مشاهده کد IL متوجه شد). نحوه استفاده از کلاس Mutex را در کد زیر که تغییر داده شده متد PrintNumbers است مشاهده می کنید:

public static void PrintNumbers()
{
    Monitor.Enter(threadLock);            
    try
    {
        Console.Write("{0} is printing numbers  >  " , Thread.CurrentThread.Name);
        for (int counter = 0; counter  <  10 ; counter++)
        {
            Thread.Sleep(200*new Random().Next(5));
            Console.Write("{0},", counter);
        }
        Console.WriteLine();
    }
    finally
    {
        Monitor.Exit(threadLock);
    }
}

متد Enter در کلاس Monitor اعلام می کند که thread ای وارد محدوده ای شده است که باید thread-safe باشد. به عنوان پارامتر ورودی برای این متد token ایجاد شده یعنی obj را ارسال می کنیم. در قدم بعدی کل کدی که مربوط به ناحیه thread-safe است باید داخل بدنه try-catch نوشته شود و البته بخش finaly نیز برای آن نوشته شده باشد، همانطور که می دانید بخش finally قسمتی از بدنه try است که در هر صورت اجرا می شود. در قسمت finally متد Exit را با پارامتر threadLock که همان token مربوطه است فراخوانی می کنیم، یعنی thread در حال اجرا از محدوده thread-safe خارج شده است. دلیل نوشتن try-catch در کد بالا این است که در صورت وقوع خطا عملیات خروج از محدوده thread-safe در هر صورت انجام شود و thread های دیگر منتظر ورود به این محدوده نمانند. شاید این سوال برای شما بوجود بیاید که با وجود کلمه کلیدی lock چه دلیلی برای استفاده از کلاس Monitor وجود دارد؟ دلیل این موضوع کنترل بیشتر بر روی ورود thread ها و اجرای کدهای thread-safe است. برای مثال، بوسیله متد Wait در کلاس Monitor می توان مشخص کرد که یک thread چه مدت زمانی را برای ورود به ناحیه thread-safe باید منتظر بماند و همچنین متدهای Pulse و PulseAll را می توان برای اطلاع رسانی به سایر thread ها در مورد اینکه کار thread جاری به اتمام رسیده استفاده کرد. البته در اکثر موقعیت ها استفاده از کلمه کلیدی lock کافی است و نیازی به استفاده از کلاس Monitor نمی باشد.

پیاده سازی Synchronization با استفاده از کلاس Interlocked

زمانی که قصد داریم مقدار یک متغیر را تغییر دهیم یا عملگرهای ریاضی را بر روی دو متغیر اعمال کنیم، عملیات های انجام شده به دو صورت Atomic و Non-Atomic انجام می شوند. مبحث عملیات عملیات های Atomic چیزی بیش از چند خط نیاز دارد تا توضیح داده شود، اما به طور خلاصه می توان گفت که عملیات های Atomic عملیات هایی هستند که تنها در یک مرحله یا step انجام می شوند. اگر کدهای IL مرتبط به مقدار دهی متغیرها و البته عملگرهای ریاضی را بر روی بیشتر نوع های داده در دات نت مشاهده کنیم میبینیم که این عملیات ها بیشتر به صورت non-atomic هستند، یعنی بیش از یک مرحله برای انجام عملیات مربوط نیاز است که این موضوع می تواند در برنامه های Multi-threaded مشکل ساز شود. در حالت ساده می توان بوسیله مکانیزم lock عملیات synchronization را برای این عملیات ها پیاده سازی کرد:

int value = 1;
lock(token)
{
    value++;
}

اما برای شرایطی مانند مثال بالا استفاده از lock یا کلاس monitor باعث ایجاد overhead اضافی می شود که برای حل این مشکل می توان از کلاس Interlocked برای اعمال synchronization در اعمال انتساب مقدار یا مقایسه مقادیر استفاده کرد. برای مثال، زمانی که می خواهیم مقدار یک متغیر را با استفاده از کلاس Interlocked اضافه کنیم به صورت می توانیم این کار را پیاده سازی کنیم:

int myNumber = 10;
Interlocked.Increment(ref myNumber);

متد Increment در کلاس Interlocked یک مقدار به متغیر مشخص شده اضافه می کند و البته این کار در محیط Thread-Safe انجام می شود. برای کاهش مقدار می توان از متد Decrement به صورت مشابه استفاده کرد:

int myNumber = 10;
Interlocked.Decrement(ref myNumber);

همچنین برای مقایسه و مقدار دهی مقدار یک متغیر می توان از متد CompareExchange استفاده به صورت زیر استفاده کرد:

int myNumber = 10;
Interlocked.CompareExchange(ref myNumber, 15, 10);

در کد بالا در صورتی که مقدار متغیر myNumber برابر 10 باشد، مقدار آن با 15 عوض خواهد شد.

پیاده سازی Synchronization بوسیله خاصیت [Synchronization]

می دانیم که خاصیت [Synchronization] زمانی که بر روی یک کلاس قرار میگیرد، باعث می شود که کد داخل آن کلاس به صورت Thread-Safe اجرا شود. در این قسمت می خواهیم کد مربوط به متد PrintNumbers را به صورت Thread-Safe و با کمک Object Context Boundry و همچنین خاصیت [Synchronization] پیاده سازی کنیم. برای این کار ابتدا یک کلاس با نام Printer پیاده سازی کرده و متد PrintNumbers را داخل آن قرار می دهیم. دقت کنید که کلاس Printer می بایست از کلاس ContextBoundObject مشتق شده باشد و خاصیت [Synchronization] بر روی آن قرار گرفته باشد:

[Synchronization]
public class Printer : ContextBoundObject
{
    public void PrintNumbers()
    {
        Console.Write("{0} is printing numbers  >  " , Thread.CurrentThread.Name);
        for (int counter = 0; counter  <  10 ; counter++)
        {
            Thread.Sleep(200*new Random().Next(5));
            Console.Write("{0},", counter);
        }
        Console.WriteLine();
    }
}

پس از انجام تغییرات بالا متد Main را به صورتی تغییر می دهیم که از متد Print در کلاس Printer استفاده کند:

Printer printer = new Printer();

Thread[] threads = new Thread[10];

for (int index = 0; index  <  10 ; index++)
{
    threads[index] = new Thread(printer.PrintNumbers);
    threads[index].Name = string.Format("Worker thread #{0}.", index);
}

foreach (var thread in threads)
{
    thread.Start();
}

Console.ReadLine();

با انجام کارهای بالا و اجرای برنامه مشاهده خواهیم کرد که با وجود عدم استفاده از کلمه کلیدی lock یا کلاس Monitor، برنامه به صورت Thread-Safe اجرا شده و خروجی مناسب برای ما تولید می شود. در اینجا مبحث مربوط به Synchronization به اتمام رسیده و در قسمت در مورد کلاس Timer در فضای نام System.Threading صحبت خواهیم کرد.

منبع


قسمت اول آموزش-برنامه نویسی Asynchronous – آشنایی با Process ها، Thread ها و AppDomain ها

قسمت دوم آموزش- آشنایی با ماهیت Asynchronous در Delegate ها

قسمت سوم آموزش-آشنایی با فضای نام System.Threading و کلاس Thread

قسمت چهارم آموزش- آشنایی با Thread های Foreground و Background در دات نت

قسمت پنجم آموزش- آشنایی با مشکل Concurrency در برنامه های Multi-Threaded و راهکار های رفع این مشکل

قسمت ششم آموزش- آشنایی با کلاس Timer در زبان سی شارپ

قسمت هفتم آموزش-آشنایی با CLR ThreadPool در دات نت

قسمت هشتم آموزش- مقدمه ای بر Task Parallel Library و کلاس Parallel در دات نت

قسمت نهم آموزش- برنامه نویسی Parallel:آشنایی با کلاس Task در سی شارپ

قسمت دهم آموزش-برنامه نویسی Parallel در سی شارپ :: متوقف کردن Task ها در سی شارپ – کلاس CancellationToken

قسمت یازدهم آموزش- برنامه نویسی Parallel در سی شارپ :: کوئری های Parallel در LINQ

قسمت دوازدهم آموزش- آشنایی با کلمات کلیدی async و await در زبان سی شارپ

قسمت سیزدهم آموزش- استفاده از متد WhenAll برای اجرای چندین Task به صورت همزمان در سی شارپ

مقدمه

فعال کردن شبکه در DVR به شما این امکان را می دهد تا از طریق یک PC از راه دور برای مشاهده آنلاین یا ذخیره فیلم های موجود اقدام نمایید. DVR ها توانایی وصل شدن به شبکه داخلی و گاهی در بعضی مدل ها توانایی وصل شدن به شبکه جهانی اینترنت را دارند.
اجرای تنظیمات و اتصال به DVR می تواند از طریق سیتم عامل های مختلف صورت بپذیرد ولی تصاویر استفاده شده در این آموزش از روی یک کامپیوتر با سیستم عامل ویندوز ایکس پی تهیه شده است.

مراحل اتصال به DVR

1. آدرس آی پی (IP)، زیر شبکه(subnet mask) و دروازه پیش فرض(default gateway) کامپیوتر خود را پیدا کنید و در جدول شماره 1 یادداشت نمایید

        الف ) صفحه command prompt را باز میکنید

command prompt

شکل شماره 1

        ب ) در صفحه cmd باز شده، دستور ipconfig را تایپ کرده و اطلاعات داده شده را در جدول 1 یادداشت نمایید.

صفحه cmd

شکل شماره 2

جدول شماره 1

شکل شماره 3-جدول شماره 1

توجه داشته باشید که اطلاعات آدرس شبکه ای کامپیوتر شما ممکن است با آنچه در تصویر آمده است متفاوت باشد. همچنین در نظر داشته باشید که آدرس IP در ویندوز های Vista و 7 با عنوان IPV4 نمایش داده می شوند.

2. آدرس آی پی دستگاه DVR خود را با استفاده از راهنمای موجود در جعبه تغییر دهید.
الف ) اتصالاتDVR را برقرار کنید. (بجز اتصال کابل شبکه)
ب ) DVR را روشن کرده و از طریق setup و گزینه Network وارد تنظیمات شبکه شوید.
ج ) امکان استفاده از DHCP را روی DVR غیر فعال کنید.
د ) یک آدرس آی پی برای دستگاه DVR خود در نظر بگیرید.
A ) دقیقا آدرس آی پی کامپیوتر خود را بجز قسمت آخر یادداشت کنید. مثلا 192.168.1.
B ) برای قسمت آخر یک عدد بین 1 تا 255 انتخاب کنید. توجه کنید که این عدد باید با عدد آخر آدرس آی پی کامپیوتر شما متفاوت باشد.
C ) در صفحه cmd با استفاده از دستور ping تست کنید که آیا این آی پی در شبکه موجود می باشد یا خیر
Ping 192.168.1.عدد انتخاب شده

دستور ping

شکل شماره 4

اگر با استفاده از دستور ping پاسخ دریافتی شبیه آنچه در شکل 5 می بینید بود، یعنی آن آدرس آی پی قبلا به یک کامپیوتر دیگر در شبکه اختصاص یافته است.
اگر بعد از استفاده از دستور ping پاسخی شبیه آنچه در شکل 6 آمده است دیدید یعنی می توانید از آن آدرس برای DVR خود استفاده نمایید.

استفاده از ادرس برای DVR

شکل شماره 5

ه ) در قسمت آی پی DVR، آدرس آی پی انتخاب شده را وارد نمایید. آدرس زیر شبکه را باید دقیقا همانند آدرس زیر شبکه کامپیوتر خود انتخاب کنید.
و) بر اساس جدول موجود در دفترچه DVR و یا اطلاعات موجود روی صفحه تنظیمات شبکه DVR پورت های مربوط به DVR را مشخص نمایید.
ز ) تنظیمات را ذخیره کرده و از منوی شبکه خارج شوید.
ح) DVR را ریستارت نمایید تا تنظیمات اعمال شود.
ط ) کابل شبکه DVR را وصل نموده و روی دستگاه کامپیوتر خود صفحه cmd را باز کنید.
ی ) با استفاده از دستور ping چک کنید که آیا ارتباط با DVR برقرار است یا خیر. بایستی جواب دستور ping شبیه آنچه در شکل 7 دیده می شود، باشد.

بررسی ارتباط با DVR

شکل شماره 6

ک ) درصورتیکه ارتباط برقرار است می توانید با وارد کردن آی پی در برنامه مورد نظر روی دستگاه کامپیوتر یا دستگاه موبایل خود و دادن پورت مشخص شده در قسمت “و” ارتباط تصویری با DVR را برقرار نمایید.
ل ) درصورتیکه می خواهید از مرورگر های اینترنتی جهت اتصال و مانیتورینگ DVR استفاده نمایید کافیست مرورگر مورد نظر (IE, firefox, chrome) را باز کرده و در قسمت address bar، آی پی   DVR را وارد کرده و در انتها با یک “:” پورت را به آن معرفی نمایید.
برای مثال اگر آی پی DVR شما 192.168.1.100 است و پورت تعریف شده 92 می باشد. آدرس 192.168.1.100:92 را در مرورگر وارد نمایید.

آدرس ای پی DVR به همراه پورت

شکل شماره 7

انتقال تصاویر روی اینترنت

بعضی از DVR ها امکان انتقال تصاویر بر روی اینترنت از طریق مرورگر یا دستگاه های موبایل را دارا می باشند.
برای این منظور نیاز به یک ارتباط پرسرعت اینترنت در محل قرارگیری دستگاه DVR و یک ارتباط پرسرعت اینترنت دیگر در محلی که قرار است مانیتورینگ انجام پذیرد، وجود دارد.
برای استفاده از ویژگی انتقال تصویر بایستی port forwarding روی روتر اینترنت محل قرارگیری دستگاه DVR انجام شود.
1. با کمک اطلاعات موجود در دفترچه راهنمای روتر (مودم ADSL) خود و یا استفاده از اطلاعات وب سایت، http://portforwarding.comروتر خود را برای انجام port forwarding تنظیم نمایید.

دقت نمایید که در پروسه port forwarding شما یک پورت داخلی دارید که همان پورت مشخص شده در دستگاه DVR است و یک پورت خارجی دارید که با راهنمایی شرکت سرویس دهنده اینترنت می توانید از پورت های خالی قابل استفاده اطلاع حاصل نمایید.
همچنین لازم است تا آی پی دستگاه DVR را به عنوان آدرس دستگاهی که port forwarding روی آن انجام می شود، وارد نمایید.

2. بعد از انجام پروسه port forwarding کافیست با مراجعه به سایت www.yougetsignal.com و استفاده از ابزار port forwarding tester بررسی نمایید که آیا پروسه با موفقیت انجام شده است یا خیر.
3. با استفاده از ابزار what is my IP در وب سایت www.yougetsignal.com یا با بازکردن سایت www.whatismyip.com روی یکی از کامپیوتر های موجود در شبکه ای که دستگاه DVR هم در آن قرار دارد، شبکه خود را بدست آورید.
4. روی کامپیوتر یا دستگاه موبایل محلی که می خواهید مانیتوریگ انجام شود کافیست همانند اینکه کامپیوتر یا موبایل در همان شبکه DVR قرار دارد، عمل نمایید تنها با این تفاوت که بجای آدرس آی پی محلی که قبلا وارد کرده اید (قسمت “ل”) آدرس آی پی ای را که از سایت www.whatismyip.com بدست آورده اید وارد می کنید و بجای پورت داخلی، پورت خارجی ای که از شرکت سرویس دهنده اینترنت دریافت کرده اید وارد می نمایید.
لازم به ذکر است مرورگرهای اینترنتی بطور پیش فرض از پورت 80 استفاده می کنند که گاهی این پورت در شبکه داخلی یا شبکه اینترنت قبلا توسط نرم افزار دیگری مورد استفاده قرار گرفته یا شرکت سرویس دهنده اینترنت جهت امنیت بیشتر این پورت را بسته است.

منبع

تاریخچه سیستمهای OCR

از جنبه تاریخی سیستم های OCR تا کنون سه مرحله تکاملی را پشت سر گذاشته اند:

ﺍﻟﻒ ) ﻣﺮحله ﺗﻜﻮﻳﻦ : (از سال 1900 تا 1980)

ﺭﺩ ﭘﺎﻱ ﺍﻭﻟﻴﺔ ﺍﻗﺪﺍﻣﺎﺕ ﺻﻮﺭﺕ ﮔﺮﻓﺘﻪ ﺩﺭ ﺯﻣﻴﻨﺔ ﺑﺎﺯﺷﻨﺎﺳﻲ حروف را ﺩﺭ ﺳﺎﻟﻬﺎﻱ ﺍﻭﻝ ﺩﻫﺔ 1900 می ﺗﻮﺍﻥ ﻳﺎﻓﺖ ﻭ ﺁﻥ ﺯﻣﺎﻧﻲ ﺍﺳﺖ ﻛﻪ Tyuring ﺩﺍﻧﺸﻤﻨﺪ ﺭوسی بر آن بود  ﻛﻪ ﺑﻪ ﺍﻓﺮﺍﺩ ﻣﺒﺘﻼ ﺑﻪ ﻧﺎﺭﺳﺎﻳﻴﻬﺎﻱ ﺑﻴﻨﺎﻳﻲ ﻛﻤﻚ ﻧﻤﺎﻳﺪ و ﺍﻭﻟﻴﻦ ﺍﺧﺘﺮﺍﻉ های ﺛﺒﺖ ﺷﺪﻩ ﺩﺭ ﺍﻳﻦ ﺯﻣﻴﻨﻪ مربوط به سالهای 1929 و 1933 هستند
ﺍﻳﻦ ﺳﻴﺴﺘﻢ ﻫﺎ ﺣﺮﻭﻑ ﭼﺎﭘﻲ ﺭﺍ ﺑﺎ ﺭﻭﺵ ﺗﻄﺎﺑﻖ ﻗﺎﻟﺒﻲ ﺷﻨﺎﺳﺎﻳﻲ ﻣﻲ كردند. ﻣﺎﺳﻜﻬﺎﻱ ﻣﻜﺎﻧﻴﻜﻲ ﻣﺨﺘﻠﻔﻲ ﺍﺯ ﻣﻘﺎﺑﻞ ﺗﺼﻮﻳﺮ ﺣﺮﻑ ﻋﺒﻮﺭ می ﻛﺮﺩﻧﺪ و نور از یك سو ﺑﻪ ﺁﻥ ﺗﺎﺑﺎﻧﺪﻩ ﺷﺪﻩ ﻭ ﺍﺯ ﺳﻮﻱ ﺩﻳﮕﺮ ﺗﻮﺳﻂ ﻳﻚ ﺁﺷﻜﺎﺭﺳﺎﺯ ﻧﻮﺭﻱ ﺩﺭﻳﺎﻓﺖ ﻣﻲ شد. وقتی یك انطباق كامل صورت می گرفت ﻧﻮﺭ ﺑﻪ ﺁﺷﻜﺎﺭﺳﺎﺯ ﻧﻤﻲ ﺭﺳﻴﺪ ﻭ ﺣﺮﻑ ﻭﺭﻭﺩﻱ ﺑﺎﺯﺷﻨﺎﺳﻲ ﻣﻲ شد. این ﺍﺧﺘﺮﺍﻉ ﺑﻪ ﺩﻟﻴﻞ ﺗﻜﻨﻮﻟﻮﮊﻱ ﺍﭘﺘﻮﻣﻜﺎﻧﻴﻜﻲ ﻣﻮﺭﺩ ﺍﺳﺘﻔﺎﺩﻩ ﺍﺯ ﺁﻧﻬﺎ ﻛﺎﺭﺑﺮﺩﻱ ﻧﺒﻮﺩ و ﺗﺼﻮﺭ ﺩﺳﺘﺮﺳﻲ ﺑﻪ ﺩﺳﺘﮕﺎﻫﻲ ﺑﺮﺍﻱ ﺑﺎﺯﺷﻨﺎﺳﻲ ﺣﺮﻭﻑ ﺗﺎ ﺩﻫﻪ 1940 ﻣﻴﻼﺩﻱ ﻭ ﻇﻬﻮﺭ ﻛﺎﻣﭙﻴﻮﺗﺮﻫﺎﻱ ﺩﻳﺠﻴﺘﺎﻝ ﺑﺼﻮﺭﺕ ﻳﻚ ﺭﺅﻳﺎ ﺑﺎﻗﻲ ﻣﺎﻧﺪ.
ﺍﻗﺪﺍﻣﺎﺕ ﺍﻭﻟﻴﻪ ﺩﺭ ﺯﻣﻴﻨﺔ ﺑﺎﺯﺷﻨﺎﺳﻲ ﺣﺮﻭﻑ، ﺑﺮ ﺭﻭﻱ ﻣﺘﻮﻥ ﭼﺎﭘﻲ ﻭ ﻳﺎ ﻣﺠﻤﻮﻋﺔ ﻛﻮﭼﻜﻲ ﺍﺯ ﺣﺮﻭﻑ ﻭ ﻧﻤﺎﺩﻫﺎﻱ ﺩﺳﺘﻨﻮﻳﺲ ﻛﻪ ﺑﺮﺍﺣﺘﻲ ﻗﺎﺑﻞ ﺗﺸﺨﻴﺺ ﺑﻮﺩﻧﺪ، ﻣﺘﻤﺮﻛﺰ ﮔﺮﺩﻳﺪﻩ ﺑﻮﺩ. ﺳﻴﺴﺘﻤﻬﺎﻱ ﺑﺎﺯﺷﻨﺎﺳﻲ ﺣﺮﻭﻑ ﭼﺎﭘﻲ ﻛﻪ ﺩﺭ ﺍﻳﻦ ﻣﻘﻄﻊ ﺯﻣﺎﻧﻲ ﻣﻌﺮﻓﻲ ﺷﺪﻧﺪ، ﻋﻤﺪﺗﺎً ﺍﺯ ﺭﻭﺵ ﺗﻄﺎﺑﻖ ﻗﺎﻟﺒﻲ ﺍﺳﺘﻔﺎﺩﻩ می نمودند ﻛﻪ ﺩﺭ ﺁﻥ ﺗﺼﻮﻳﺮ ﻭﺭﻭﺩﻱ ﺑﺎ ﻛﺘﺎﺑﺨﺎﻧﻪ ای از تصاویر ﺣﺮﻭﻑ ﻣﻮﺭﺩ ﻣﻘﺎﻳﺴﻪ ﻗﺮﺍﺭ ﻣﻲ گرفت. در ﻣﻮﺭﺩ ﻣﺘﻮﻥ ﺩﺳﺘﻨﻮﻳﺲ ﻧﻴز ﺍﻟﮕﻮﺭﻳﺘﻤﻬﺎﻱ ﭘﺮﺩﺍﺯﺵ ﺗﺼﻮﻳﺮ ﻛﻪ ﻭﻳﮋﮔﻴﻬﺎﻱ ﺳﻄﺢ ﭘﺎﻳﻴﻦ را از تصاویر ﺍﺳﺘﺨﺮﺍﺝ ﻣﻲ كنند، ﺑﻪ ﺗﺼﺎﻭﻳﺮ ﺩﻭﺩﻭﻳﻲ ﺍﻋﻤﺎﻝ ﻣﻲ ﺷﺪ ﺗﺎ ﺑﺮﺩﺍﺭﻫﺎﻱ ﻭﻳﮋﮔﻲ ﺍﺳﺘﺨﺮﺍﺝ ﮔﺮﺩﻧﺪ. ﺳﭙﺲ ﺍﻳﻦ ﺑﺮﺩﺍﺭﻫﺎﻱ ﻭﻳﮋﮔﻲ ﺑﻪ ﻃﺒﻘﻪ ﻛﻨﻨﺪﻩ ﺁﻣﺎﺭﻱ ﺳﭙﺮﺩﻩ ﻣﻲ ﺷﺪﻧﺪ.
ﺩﺭ ﺍﻳﻦ ﺩﻭﺭﻩ، ﺗﺤﻘﻴﻘﺎﺕ ﻣﻮﻓﻖ ﺍﻣﺎ ﻣﻘﻴﺪﻱ ﺑﻴﺸﺘﺮ ﺑﺮ ﺭﻭﻱ ﺣﺮﻭﻑ ﻭ ﺍﻋﺪﺍﺩ لاتین ﺍﻧﺠﺎﻡ ﮔﺮﻓﺖ با ﺍﻳﻦ ﻭﺟﻮﺩ ﻣﻄﺎﻟﻌﺎﺕ ﭼﻨﺪﻱ ﻧﻴﺰ ﺩﺭ ﺯﻣﻴﻨﺔ ﺣﺮﻭﻑ ﮊﺍﭘﻨﻲ، ﭼﻴﻨﻲ، ﻋﺒﺮﻱ، ﻫﻨﺪﻱ، ﺳﻴﺮﻳﻠﻴﻜﻲ، ﻳﻮﻧﺎﻧﻲ ﻭ ﻋﺮﺑﻲ ﺩﺭ ﻫﺮ ﺩﻭ ﺯﻣﻴﻨﺔ ﺣﺮﻭﻑ ﭼﺎﭘﻲ ﻭ ﺩﺳﺘﻨﻮﻳﺲ ﺁﻏﺎﺯ ﮔﺮﺩﻳﺪ ﺑﺎ ﻇﻬﻮﺭ ﺻﻔﺤﺎﺕ ﺭﻗﻮﻣﻲ كننده ( ﺩﻳﺠﻴﺘﺎﻳﺰﺭﻫﺎ ) در دهه 1950 كه ﻗﺎﺩﺭ ﺑﻪ ﺗﺸﺨﻴﺺ ﻣﺨﺘﺼﺎﺕ ﺣﺮﻛﺘﻲ ﻧﻮﻙ ﻳﻚ ﻗﻠﻢ ﻣﺨﺼﻮﺹ ﺑﻮﺩﻧﺪ، ﺳﻴﺴﺘﻤﻬﺎﻱ OCR ﺗﺠﺎﺭﻱ ﻧﻴﺰ ﺍﻣﻜﺎﻥ ﻋﺮﺿﻪ ﻳﺎﻓﺘﻨﺪ. ﺍﻳﻦ ﻧﻮﺁﻭﺭﻱ ﺳﺒﺐ ﺷﺪ ﻛﻪ ﻣﺤﻘﻘﺎﻥ ﺑﺘﻮﺍﻧﻨﺪ ﺩﺭ ﺯﻣﻴﻨﺔ بازشناسایی حروف دست نویس فعالیت خود را آغاز نمایند.

ب ) ﻣﺮحله توسعه: (از سال 1980 تا 1990) 

ﻣﻄﺎﻟﻌﺎﺕ ﺻﻮﺭﺕ ﮔﺮﻓﺘﻪ ﺗﺎ ﻗﺒﻞ ﺍﺯ ﺳﺎﻝ 1980 ﺍﺯ ﻓﻘﺪﺍﻥ سخت افزارهای ﻛﺎﻣﭙﻴﻮﺗﺮﻱ ﻗﺪﺭﺗﻤﻨﺪ ﻭ ﺩﺳﺘﮕﺎﻫﻬﺎﻱ ﺍﺧﺬ ﺩﺍﺩﻩها رنج می بردند. در ﺍﻳﻦ ﺩﻫﻪ ﺑﻮﺍﺳﻄﺔ ﺭﺷﺪ ﺍﻧﻔﺠﺎﺭﮔﻮﻧﺔ ﺗﻜﻨﻮﻟﻮﮊﻱ اطلاعات، ﻭﺿﻌﻴﺖ ﺑﺴﻴﺎﺭ ﻣﻨﺎﺳﺒﻲ ﺑﺮﺍﻱ ﺯﻣﻴﻨﻪ ﻫﺎﻱ ﺗﺤﻘﻴﻘﺎﺗﻲ ﻣﺨﺘﻠﻒ ﺍﺯ ﺟﻤﻠﻪ ﺑﺎﺯﺷﻨﺎﺳﻲ ﺣﺮﻭﻑ ﻓﺮﺍﻫﻢ ﮔﺮﺩﻳﺪ. ﺭﻭﺷﻬﺎﻱ ﺳﺎﺧﺘﺎﺭﻱ ﺑﻪ ﻫﻤﺮﺍﻩ ﺭﻭﺷﻬﺎﻱ ﺁﻣﺎﺭﻱ ﺩﺭ ﺑﺴﻴﺎﺭﻱ ﺍﺯ ﺳﻴﺴﺘﻤﻬﺎ ﻇﺎﻫﺮ ﮔﺮﺩﻳﺪﻧﺪ.  ﺗﺤﻘﻴﻘﺎﺕ ﺩﺭ ﺯﻣﻴﻨﺔ OCR ﺍﺳﺎﺳﺎً ﺗﻮﺟﻪ ﺧﻮﺩ ﺭﺍ ﺑﻪ ﺭﻭﺷﻬﺎﻱ ﺑﺎﺯﺷﻨﺎﺳﻲ ﺍﺷﻜﺎﻝ ﺑﺪﻭﻥ ﺗﻮﺟﻪ ﺑﻪ ﻫﺮﮔﻮﻧﻪ اطلاعات ﻣﻌﻨﺎﺷﻨﺎﺧﺘﻲ ﻣﻌﻄﻮﻑ ﻧﻤﻮﺩ. این مسئله سبب ﮔﺮﺩﻳﺪ ﻛﻪ ﻧﺮﺥ ﺑﺎﺯﺷﻨﺎﺳﻲ نتواند ﺍﺯ ﻳﻚ ﺣﺪ ﺧﺎﺹ ﻓﺮﺍﺗﺮ بروﺩ. ﻛﻪ ﺩﺭ ﺑﺴﻴﺎﺭﻱ ﺍﺯ ﻛﺎﺭﺑﺮﺩﻫﺎﻱ OCR ﻗﺎﺑﻞ ﻗﺒﻮﻝ ﻧﺒﻮﺩ.

ج ) ﻣﺮحله بهبود: (از سال 1990 به بعد) 

ﺩﺭ ﺍﻳﻦ ﻣﻘﻄﻊ ﺯﻣﺎﻧﻲ ﺑﻮﺩ ﻛﻪ ﺑﺎ ﺗﻜﻮﻳﻦ ﺍﺑﺰﺍﺭﻫﺎ ﻭ ﺗﻜﻨﻴﻜﻬﺎﻱ ﭘﺮﺩﺍﺯﺷﻲ ﺟﺪﻳﺪ، ﭘﻴﺸﺮﻓﺖ ﻭﺍﻗﻌﻲ ﺳﻴﺴﺘﻤﻬﺎﻱ OCR ﻣﺤﻘﻖ ﮔﺮﺩﻳﺪ. ﺩﺭ ﺍﻭﺍﻳﻞ ﺩﻫﺔ 1990 ﺭﻭﺷﻬﺎﻱ ﭘﺮﺩﺍﺯﺵ ﺗﺼﻮﻳﺮ ﻭ ﺑﺎﺯﺷﻨﺎﺳﻲ ﺍﻟﮕﻮ ﺑﺎ ﺗﻜﻨﻴﻜﻬﺎﻱ ﻛﺎﺭﺁﻣﺪ ﻫﻮﺵ ﻣﺼﻨﻮﻋﻲ (AI) ﺍﺩﻏﺎﻡ ﮔﺸﺘﻨﺪ. ﻣﺤﻘﻘﺎﻥ ﺍﻟﮕﻮﺭﻳﺘﻤﻬﺎﻱ ﺑﺎﺯﺷﻨﺎﺳﻲ ﺣﺮﻭﻑ ﭘﻴﭽﻴﺪﻩ ﺭﺍ ﺍﺑﺪﺍع ﻧﻤﻮﺩﻧﺪ ﻛﻪ ﻗﺎﺩﺭ بودند ﺩﺍﺩﻩ ﻫﺎﻱ ﻭﺭﻭﺩﻱ ﺑﺎ ﺗﻔﻜﻴﻚ پذیری ﺑﺎﻻ ﺭﺍ ﺩﺭﻳﺎﻓﺖ ﻛﻨﻨﺪ ﻭ ﺩﺭ ﻣﺮﺣﻠﺔ ﭘﻴﺎﺩﻩ سازی، ﻣﺤﺎﺳﺒﺎﺕ ﺑﺴﻴﺎﺭ ﺯﻳﺎﺩﻱ ﺭﺍ ﺑﺮ ﺭﻭﻱ ﺩﺍﺩﻩ ﺍﻧﺠﺎﻡ ﺩﻫﻨﺪ. كه عبارتند از شبكه های عصبی ، منطق فازی و پردازش زبانهای طبیعی و غیره.

منبع