مشکل در دریافت و ارسال اطلاعات از طریق پورت سریال c#

pb559blue

Active Member
سلام دوستان.

من برنامه‌ای برای تنظیم یک دستگاه نوشته‌ام که در مرحله پروگرام کردن و ارسال تنظیمات به دستگاه با مشکل مضحکی روبرو شدم و ازش سز در نمیارم!!!

روند کا اینه که:

1) من روی دکمه "اتصال" کلیک میکنم و کاراکتری رو به دستگاه میفرستم.
2) در جواب ارسال من دستگاه کاراکتری رو برای من ارسال میکنه که من روی DataRecieved سریال پورت اون رو میگیرم. (این دو مرحله تنها برای اینه که من مطمئن شم به دستگاه درستی داده میفرستم)
3) بعد دکمه اتصال رو غیر فعال میکنم و دکمه "ارسال" رو فعال میکنم.
4) با فشار دادن دکمه ارسال من شروع به فرستادن اولین رشته تنظیمات میکنم برای دستگاه.
5) دستگاه بعد از تایید تنظیمات کاراکتری رو برای من میفرسته تا من مطمئن بشم که داده رو بدرستی دریافت کرده.
6) و من رشته بعدی رو ارسال میکنم.
مراحل 6 و 5 تا پایان ارسال آخرین رشته تنظیمات ادامه پیدا میکنه و من پورت رو میبندم.

اما مشکل اینجاست که مراحل 5 و 6 وقتی که من روی Title Bar فورم دابل کلیکی میکنم فعال میشن!!!
یعنی گاهی تا 10تا رشته، گاهی تا 50تا رشته و ... روند بدون مشکل ادامه پیدا میکنه اما ناگهان متوقف میشه و با فعال کردن فرم و کلیک روی Title Bar دوباره ادامه پیدا میکنه.

انگار که برنامه من در این مدت هیچ ارتباطی با CPU نداشته باشه!!!

امیدوارم متوجه شده باشید که اصلا مشکلم چی هست، حالا راه حل پیشکش.
 

the_king

مدیرکل انجمن
سلام دوستان.

من برنامه‌ای برای تنظیم یک دستگاه نوشته‌ام که در مرحله پروگرام کردن و ارسال تنظیمات به دستگاه با مشکل مضحکی روبرو شدم و ازش سز در نمیارم!!!

روند کا اینه که:

1) من روی دکمه "اتصال" کلیک میکنم و کاراکتری رو به دستگاه میفرستم.
2) در جواب ارسال من دستگاه کاراکتری رو برای من ارسال میکنه که من روی DataRecieved سریال پورت اون رو میگیرم. (این دو مرحله تنها برای اینه که من مطمئن شم به دستگاه درستی داده میفرستم)
3) بعد دکمه اتصال رو غیر فعال میکنم و دکمه "ارسال" رو فعال میکنم.
4) با فشار دادن دکمه ارسال من شروع به فرستادن اولین رشته تنظیمات میکنم برای دستگاه.
5) دستگاه بعد از تایید تنظیمات کاراکتری رو برای من میفرسته تا من مطمئن بشم که داده رو بدرستی دریافت کرده.
6) و من رشته بعدی رو ارسال میکنم.
مراحل 6 و 5 تا پایان ارسال آخرین رشته تنظیمات ادامه پیدا میکنه و من پورت رو میبندم.

اما مشکل اینجاست که مراحل 5 و 6 وقتی که من روی Title Bar فورم دابل کلیکی میکنم فعال میشن!!!
یعنی گاهی تا 10تا رشته، گاهی تا 50تا رشته و ... روند بدون مشکل ادامه پیدا میکنه اما ناگهان متوقف میشه و با فعال کردن فرم و کلیک روی Title Bar دوباره ادامه پیدا میکنه.

انگار که برنامه من در این مدت هیچ ارتباطی با CPU نداشته باشه!!!

امیدوارم متوجه شده باشید که اصلا مشکلم چی هست، حالا راه حل پیشکش.
قاعدتا کاری در کد تان انجام می دهید که رخداد های فرم رویش تاثیر داره. معمولا مشکل اصلی در ارتباط های پورت سریال اینه که ارتباط به دلیل سرعت بالای برنامه سنکرون نیست و پورت و دستگاه هنوز ارسال قبلی رو دریافت نکرده
برنامه برای پاسخ Timeout دریافت می کنه و داده بعدی یا همون قبلی رو مجددا می فرسته.
شما بعد از مرحله 4 که اولین رشته رو به دستگاه می فرستید با چه شیوه ای ارتباط بین برنامه و دستگاه رو هماهنگ می کنید؟ در کد تون در نظر می گیرید که پورت و دستگاه نسبت به سرعت برنامه شما خیلی کند عمل می کنند
و ارسال باید با سرعت پایین و بین ارسال ها مکث داشته باشید؟
 

pb559blue

Active Member
من یک تایمر گذاشتم با وقفه 2میلی ثانیه، و رشته‌ام رو با این وقفه سلسله‌وار برای دستگاه میفرستم. کد دستگاه طوری نوشته شده که رشته 3بار رشته ارسالی من رو میگیره و تست میکنه که هر سه آنها برابر باشند. بعد از تست پیغام ok برای من میفرسته.
 

pb559blue

Active Member
این کدیه که من روی دکمه اتصال نوشتم:

کد:
[LEFT]try
{
    comPort.DataReceived += new SerialDataReceivedEventHandler(dataRcvd);
    comPort.Open();

    connStablished = true;
    iterator = -1;
    connectBtn.Enabled = false;
    progBar.Enabled = true;
    progBar.Style = ProgressBarStyle.Marquee;
    progBar.Value = 0;

    this.BeginInvoke(new SetTextCallback(SetText), new object[] { "S" });
}
catch (System.UnauthorizedAccessException ex)
{
    sendBtn.Enabled = false;
    connectBtn.Enabled = true;
    MessageBox.Show("دسترسی به پورت انتخاب شده ممکن نیست");
}
           [/LEFT]

من تابع SetText رو توی DataReceived هم صدا می‌زنم که مقدار OK رو میگیره و در یک شرط بررسی میکنه اگر مقدار برگشتی OK بود رشته بعدی رو بفرست اگر S بود کار دیگه‌ای بکن اگر N بود کانکشن رو Close کن و...
جالب اینه که من گاهی از Task Manager قسمت Processها وقتی برنامه System Idle Process رو می‌بندم (که البته Error میده و بسته هم نمیشه) برنامه من داده جابهجا میکنه!
 
آخرین ویرایش:

the_king

مدیرکل انجمن
من یک تایمر گذاشتم با وقفه 2میلی ثانیه، و رشته‌ام رو با این وقفه سلسله‌وار برای دستگاه میفرستم. کد دستگاه طوری نوشته شده که رشته 3بار رشته ارسالی من رو میگیره و تست میکنه که هر سه آنها برابر باشند. بعد از تست پیغام ok برای من میفرسته.

اگه از Timer استفاده کرده باشید که کلا برای زمان های کم حساب کتاب نداره.
Timer برای وقفه های بزرگ قابل استفاده است ولی اصلا دقت نداره. برای 2 میلی ثانیه که اصلا مناسب نیست. دو میلی ثانیه یعنی هر ثانیه 500 بار.
این کد رو امتحان کنید ببینید دقت Timer چقدر ئه و 500 بار اجرای تایمر چند ثانیه طول میکشه، به مراتب بیشتر از یک ثانیه (کد تایمر وقتی 500 بار اجرا شد زمان سپری شده را نشان خواهد داد)

کد:
[COLOR="#A9A9A9"]    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
[/COLOR]
        private static int tick = 0;

        private void Form1_Load(object sender, EventArgs e)
        {
            var t = new Timer()
            {
                Interval = 2,
            };
            t.Tick += (t_Tick);
            t.Tag = DateTime.Now;
            t.Start();
        }

        void t_Tick(object sender, EventArgs e)
        {
            tick++;
            if (tick == 500)
            {
                var t = (Timer)sender;
                t.Stop();
                var s = DateTime.Now.Subtract((DateTime)t.Tag).TotalSeconds;
                MessageBox.Show(string.Format("{0} seconds", s));
            }
        }
[COLOR="#A9A9A9"]    }[/COLOR]
 

pb559blue

Active Member
من حتی وقتی Firefox رو اجرا میکنم، انتقال داده تو برنامه من ادامه پیدا میکنه!!!
 

the_king

مدیرکل انجمن
من حتی وقتی Firefox رو اجرا میکنم، انتقال داده تو برنامه من ادامه پیدا میکنه!!!

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

pb559blue

Active Member
کد سمت دستگاه رو من ننوشتم، مقادیرم رو هم با و سرعت تایمر رو هم با اون شخص هماهنگ کردم.
البنه من هیچ کدوم از این مشکلات رو وقتی با HyperTerminal یا برنامه‌های مشابه مثل TeraTerm، RealTerm و Putty برنامه خودم رو تست میکنم ندارم و همه درست اناجم میشه. واضحه تو این برنامه‌ها من مقدار برگشتی رو دستی ارسال میکنم.

حالا به نظر شما من چکار باید بکنم! واقعا گیج شدم.

البته این رو هم بگم که من وقتی ok گرفتم تایمر متوقف میکنم و بعد برای ارسال داده بعدی دوباره فعالش میکنم.
اگر تایمر رو حذف کنم، به چه روشی میتونم رشته‌های خودم رو مرتب برای دستگاه بفرستم. کد سمت دستگاه برای اطمینان حاصل کردن از درستی رشته‌های ارسالی اصرار داده که هر رشته رو حتما سه بار دریافت کنه و آزمایش کنه! برای همین از من خواسته تا رشته‌هام رو سلسله‌وار براش ارسال کنم.
 
آخرین ویرایش:

the_king

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

کدی که قراره تو تایمر اجرا بشه رو از داخل تایمر دربیاورید و داخل یک حلقه قرار بدید، حالا حلقه for یا while یا do while و ...
وقتی قراره مکث کنید، از Sleep استفاده کنید :
کد:
System.Threading.Thread.Sleep(2);

و هر جا که قراره برنامه تون مقداری رو به پورت بفرسته و یا از پورت بخونه، قبلش مکث کنید. ممکنه لازم بشه از مکث هایی بزرگتر از 2 میلی ثانیه هم استفاده کنید.
 

pb559blue

Active Member
من با حلقه که اصلا جوابی دریافت نمیکنم!!!

رویداد DataReceived اجرا میشه اما پیغامی که تو این رویداد دریافت میکنم همون رشته‌ای هست که خودم ارسال کردم! در حالی که باید مقدار K رو دریافت کنم!

این کد داخل DataReceived isja:

کد:
[LEFT]
private void dataRcvd(object sender, SerialDataReceivedEventArgs e)
{
    int a = 0;
    string callbackMsg = string.Empty;
   
     try
    {
        callbackMsg = ((SerialPort)sender).ReadExisting();
      
        if (callbackMsg != String.Empty)
        {
            if (!connStablished)
            {
                a = callbackMsg.IndexOf("K");
                if(a >= 0)
                {
                    this.BeginInvoke(new SetTextCallback(SetText), new object[] { callbackMsg[a].ToString() });
                    callbackMsg = string.Empty;
                }
            }
            else
            {
                this.BeginInvoke(new SetTextCallback(SetText), new object[] { callbackMsg[0].ToString() });
                callbackMsg = string.Empty;
            }
        }
    }
    catch (SystemException ex)
    {
        MessageBox.Show("خطا در دریافت پاسخ از دستگاه");
    }
}[/LEFT]


حالا من توی تابع SetText یک if ... else if دارم که اونجا بر اساس مقداری که توی DataReceieved دریافت کردم کاری رو انجام میدم.

اگر این مقدار K بود به شمارنده یکی اضافه میکنم و رشته‌ام (رشته‌هام رو توی یک آرایه ذخیره کردم) ارسال میکنم واسه سریال پورت.
حالا اینجا اگر حلقه for بذارم، دستگاه هیچ وقت K برام نمی‌فرسته. حلقه while و یا do...while هم نتونستم بذارم چون شرط مناسبی برای اتمام حلقه و اضافه کردن i پیدا نکردم. (i حتما باید بیرون حلقه و فقط یکبار اضافه بشه، چون من هر خونه آرایه‌ام رو باید چندین بار برای دستگاه ارسال کنم؛ تا وقتی که دستگاه کاراکتر K رو برای من بفرسته. اونوقت من دوباره i رو یکی اضافه میکنم و خونه بعدی آرایه‌ام رو سلسله‌وار ارسال میکنم.)
اینکار اینقدر باید تکرار بشه تا من تمام خونه‌های آرایه‌ام رو با موفقیت ارسال کنم. بعد پورت رو میبندم.


این کد تابع SetText هست:

وقتی ارتباط برقرار شد دستگاه کاراکتر Y رو برای من ارسال میکنه که من تو شرط اول اون رو بررسی میکنم.
بعد اگر کاراکتر ارسالی K باشه:
1) اول بررسی میکنم اگر آرایه داده‌ها به انتها نرسیده باشه اول تایمر رو متوقف می‌کنم؛ بعد به i یکی اضافه میکنم و اون خونه آرایه‌رو می‌گذارم تو تایمر و هر 2 میلی‌ثانیه ارسالش می‌کنم.
2) اگر هم آرایه به آخر رسیده باشه، یعنی کار من تمومه، پس پورا رو می‌بندم.

کد:
[LEFT]private void SetText(string str)
{
    char condition = Convert.ToChar(str);
  
    if (connStablished)
    {
        if ((int)condition == 89)
        {
            if (pictureBox1.BackColor != Color.Green)
            {
                pictureBox1.BackColor = Color.Green;
                connStatus.Text = "ارتباط برقرار شد";
                sendBtn.Enabled = true;
                connStablished = false;
            }
        }
    }
    else if (!connStablished)
    {
        if ((int)condition == 75)
        {
            timer.Stop();
                    
            if ( i < programData.Count - 1)
            {
                i++;
                progressBar.Value = ((i+ 1) * 100) / programData.Count;
                
                comPort.WriteLine("p" + paramStart + i + "=" + programData[ i] + "b" + Environment.NewLine); (تایمر این خط رو تکرار می‌کنه)
                timer.Start();
            }

            else if (i>= programData.Count - 1)
            {
                timer.Stop();
                comPort.Close();

                connStatus.Text = "جابجایی داده‌ها با موفقیت انجام شد";
                i = -1;
                comPort.DataReceived -= new SerialDataReceivedEventHandler(dataRcvd);
            }
        }
    }
}[/LEFT]


و این رو هم اضافه کنم که:
اگر من هر رشته رو فقط یکبار برای دستگاه بفرستم، و کد سمت دستگاه با دریافت داده (درست یا غلط) مقدار K رو برای من ارسال کنه، رد و بدل کردن داده‌ها بدرستی انجام میشه.
مشکل از اینجا شروع میشه که برنامه نویس سمت دستگاه اصرار داره که من هر رشته‌ام رو پشت سر هم بفرستم تا اون حتما حداقل سه بار هر رشته رو با مقدار درستش دریافت کنه، بعد K رو برای من بفرسته.
حالا من تا الان با تایمر اینکار رو کردم اما در ارسال داده‌ها و دریافت پیغام از سمت دستگاه مشکل داشتم. (بعد از ارسال چند رشته باموفقیت، عمل رد و بدل کردن داده‌ها متوقف میشد. error ی دریافت نمیکردم. فقط توقف)
جالب اینه که بعد از توقف، اگر من مثلا Firefox یا Chrome یا هر برنامه ای رو باز کنم، رد و بدل شدن داده‌ها ادامه پیدا میکنه. بعد دوباره متوقف میشه و بعد باز باید ی برنامه‌ای رو باز کنم :shock:
 
آخرین ویرایش:

the_king

مدیرکل انجمن
من با حلقه که اصلا جوابی دریافت نمیکنم!!!

رویداد DataReceived اجرا میشه اما پیغامی که تو این رویداد دریافت میکنم همون رشته‌ای هست که خودم ارسال کردم! در حالی که باید مقدار K رو دریافت کنم!

حالا من توی تابع SetText یک if ... else if دارم که اونجا بر اساس مقداری که توی DataReceieved دریافت کردم کاری رو انجام میدم.

اگر این مقدار K بود به شمارنده یکی اضافه میکنم و رشته‌ام (رشته‌هام رو توی یک آرایه ذخیره کردم) ارسال میکنم واسه سریال پورت.
حالا اینجا اگر حلقه for بذارم، دستگاه هیچ وقت K برام نمی‌فرسته. حلقه while و یا do...while هم نتونستم بذارم چون شرط مناسبی برای اتمام حلقه و اضافه کردن i پیدا نکردم. (i حتما باید بیرون حلقه و فقط یکبار اضافه بشه، چون من هر خونه آرایه‌ام رو باید چندین بار برای دستگاه ارسال کنم؛ تا وقتی که دستگاه کاراکتر K رو برای من بفرسته. اونوقت من دوباره i رو یکی اضافه میکنم و خونه بعدی آرایه‌ام رو سلسله‌وار ارسال میکنم.)
اینکار اینقدر باید تکرار بشه تا من تمام خونه‌های آرایه‌ام رو با موفقیت ارسال کنم. بعد پورت رو میبندم.

وقتی ارتباط برقرار شد دستگاه کاراکتر Y رو برای من ارسال میکنه که من تو شرط اول اون رو بررسی میکنم.
بعد اگر کاراکتر ارسالی K باشه:
1) اول بررسی میکنم اگر آرایه داده‌ها به انتها نرسیده باشه اول تایمر رو متوقف می‌کنم؛ بعد به i یکی اضافه میکنم و اون خونه آرایه‌رو می‌گذارم تو تایمر و هر 2 میلی‌ثانیه ارسالش می‌کنم.
2) اگر هم آرایه به آخر رسیده باشه، یعنی کار من تمومه، پس پورا رو می‌بندم.

و این رو هم اضافه کنم که:
اگر من هر رشته رو فقط یکبار برای دستگاه بفرستم، و کد سمت دستگاه با دریافت داده (درست یا غلط) مقدار K رو برای من ارسال کنه، رد و بدل کردن داده‌ها بدرستی انجام میشه.
مشکل از اینجا شروع میشه که برنامه نویس سمت دستگاه اصرار داره که من هر رشته‌ام رو پشت سر هم بفرستم تا اون حتما حداقل سه بار هر رشته رو با مقدار درستش دریافت کنه، بعد K رو برای من بفرسته.
حالا من تا الان با تایمر اینکار رو کردم اما در ارسال داده‌ها و دریافت پیغام از سمت دستگاه مشکل داشتم. (بعد از ارسال چند رشته باموفقیت، عمل رد و بدل کردن داده‌ها متوقف میشد. error ی دریافت نمیکردم. فقط توقف)
جالب اینه که بعد از توقف، اگر من مثلا Firefox یا Chrome یا هر برنامه ای رو باز کنم، رد و بدل شدن داده‌ها ادامه پیدا میکنه. بعد دوباره متوقف میشه و بعد باز باید ی برنامه‌ای رو باز کنم :shock:

دو تا مساله تو کد تون می بینم که باید بهش توجه بشه، اول اینکه شما برای اینکه از پورت دریافت مقدار کنید روی رخداد DataReceived حساب کرده اید. اشکال کار این رخداد اینه که همیشه موقع رسیدن یک کاراکتر داده رخ نمیده، کد شما توقع داره
که با هر بار اجرا شدن کد این رخداد یک کاراکتر رو بررسی کنه، در حالی که خود مایکروسافت در MSDN تاکید کرده که ضمانت نمی کنه که DataReceived با دریافت هر بایت داده رخ بده :
The DataReceived event is not gauranteed to be raised for every byte received. Use the BytesToRead property to determine how much data is left to be read in the buffer.​

نکته دوم اینه که شما هم از WriteLine استفاده کرده اید و هم در انتهای رشته Environment.NewLine نوشته اید. بصورت پیشفرض WriteLine آخر رشته کاراکتر Line feed رو بصورت خودکار می فرسته که کد کاراکتر اش 10 ئه.
اون NewLine ای رو هم خودتون دستی می نویسید دو کاراکتر Carriage return و Line feed رو می فرسته که به ترتیب کد های 13 و 10 ئه. وقتی هم WriteLine استفاده می کنید و انتهای رشته NewLine می نویسید سه کاراکتر به ترتیب 13 و سپس 10 و مجددا 10 رو می فرستید.
آیا این جزئی از پروتکل ارتباطی تونه که باید انتهای رشته های ارسالی تون سه کاراکتر 13 و 10 و 10 باشه؟
 

pb559blue

Active Member
در مورد مسئله دوم: نه؛ ارسال سه کاراکتر 13، 10 و 10 جزئی از پروتکل ارتباطی ما نیست. من تنها باید یک کاراکتر 10 یا 13 در انتهای رشته ارسال کنم. اما برنامه نویس دستگاه به کاراکتر 10 که دستور WriteLine خودش ارسال میکنه اعتمادی نداره. اون اصرار داره وفتی داده‌ها ارسال میشن، باید حتما دقیقا زیر هم مرتب بشن. وقتی از برنامه HyperTerminal برای تست کردن ارسال‌هام استفاده میکنم و ارسال رو تنها با دستور WriteLine انجام میدم خروجی شبیه به این دارم:

کد:
[LEFT]
A
     A
          A
                A
                       A
                             A
                                   A
                                          A
A
     A
           A
                  A
                          A

و الی آخر ...
[/LEFT]

اما وقتی بعلاوه WriteLine یک دستور Newline هم خودم به انتها اضافه کنم:

کد:
[LEFT]
A
A
A
A
A
و الی آخر ....
[/LEFT]

برنامه نویس دستگاه میگه در حالت اول من کاراکتر‌های SPACE اضافی ارسال میکنم و اون قادر به پردازش داده‌ها نیست (که البته من Space ای اضافه نمیکنم و بعید میدونم WriteLine خودش اینکار رو بکنه)

درباره مورد اول باید بگم که من تو تابع DataReceived با این مسئله روبرو هستم. اینکه چرا این تابع مقدار ارسالی من رو برای خودم اکو میکنه! این در حالیه که کد سمت دستگاه ادعا داره هرگز این مقدار رو دوباره به خودم بر نمیگردونه!
برای دریافت داده‌ها بدون این رویداد چه کدی باید بنویسم و چه نوع تغییری لازمه بدم! من الان چندین روزه که تمام ویدئو‌های Youtube و تعدادی مقاله در مورد ارسال داده با سریال پورت خوندم اما چیزی جز این ندیدم!!!
 
آخرین ویرایش:

the_king

مدیرکل انجمن
در مورد مسئله دوم: نه؛ ارسال سه کاراکتر 13، 10 و 10 جزئی از پروتکل ارتباطی ما نیست. من تنها باید یک کاراکتر 10 یا 13 در انتهای رشته ارسال کنم. اما برنامه نویس دستگاه به کاراکتر 10 که دستور WriteLine خودش ارسال میکنه اعتمادی نداره. اون اصرار داره وفتی داده‌ها ارسال میشن، باید حتما دقیقا زیر هم مرتب بشن. وقتی از برنامه HyperTerminal برای تست کردن ارسال‌هام استفاده میکنم و ارسال رو تنها با دستور WriteLine انجام میدم خروجی شبیه به این دارم:

کد:
[LEFT]
A
     A
          A
                A
                       A
                             A
                                   A
                                          A
A
     A
           A
                  A
                          A

و الی آخر ...
[/LEFT]

اما وقتی بعلاوه WriteLine یک دستور Newline هم خودم به انتها اضافه کنم:

کد:
[LEFT]
A
A
A
A
A
و الی آخر ....
[/LEFT]

برنامه نویس دستگاه میگه در حالت اول من کاراکتر‌های SPACE اضافی ارسال میکنم و اون قادر به پردازش داده‌ها نیست (که البته من Space ای اضافه نمیکنم و بعید میدونم WriteLine خودش اینکار رو بکنه)

درباره مورد اول باید بگم که من تو تابع DataReceived با این مسئله روبرو هستم. اینکه چرا این تابع مقدار ارسالی من رو برای خودم اکو میکنه! این در حالیه که کد سمت دستگاه ادعا داره هرگز این مقدار رو دوباره به خودم بر نمیگردونه!
برای دریافت داده‌ها بدون این رویداد چه کدی باید بنویسم و چه نوع تغییری لازمه بدم! من الان چندین روزه که تمام ویدئو‌های Youtube و تعدادی مقاله در مورد ارسال داده با سریال پورت خوندم اما چیزی جز این ندیدم!!!
به احتمال زیاد اگه بجای WriteLine با Write کار کنید و مستقیما NewLine رو درج کنید مشکل فاصله بر طرف میشه چون به هر حال WriteLine بصورت پیشفرض فقط n\ رو می فرسته.

من روی سیستم ام شبیه ساز برای پورت COM دارم، کد ام رو با اطلاعاتی که از دستگاه دادید تست کردم ولی به هر حال نمی توانم شرایط دستگاه شما رو موقع کد نویسی داشته باشم، تست کردن با دستگاه شما کاری است که باید انجام بدید.
یک متد SendTextToPort نوشتم که یک رشته رو بدون اضافه کردن هیچ کاراکتر اضافی ارسال می کته و اونقدر ارسال رو تکرار می کنه که یا پاسخ مناسب رو به تعداد لازم دریافت کنه و یا تعداد تلاش ها به حد مشخصی برسه.
اگر شرایط ایده آل باشه با همون تعداد تکرار مطلوب پاسخ مناسب رو دریافت می کنه و نیازی به تلاش های دیگه نداره و متد خاتمه پیدا می کنه، ولی اگر به هر دلیلی تلاش های ناموفق داشته باشه تا یک تعداد مشخصی به ارسال ادامه میده تا بلکه موفق بشه.
فرضا ('SendTextToPort(comPort, "test\r\n", 3, 20, 'K رشته test رو که آخرش کاراکتر های 13 و 10 داره رو به پورت comPort ارسال می کنه (پورت رو قبلا باید باز کرده باشید و تنظیمات مرتبط با پورت انجام شده باشه) و انتظار داره تا 3 بار کاراکتر K رو در پاسخ
از پورت دریافت کنه. تعداد تلاش هایش حداکثر 20 خواهد بود و در نهایت تعداد مواردی که پاسخ K دریافت شده رو به عنوان مقدار بازگشتی بر می گردونه. مثلا 3 یا 1. اگر 3 بود یعنی موفق شده و اگر 1 بود یعنی در 20 بار تلاش فقط یکبار دستگاه بهش پاسخ K داده.

کد:
        public int SendTextToPort(SerialPort port, string data, int minTry, int maxTry, char okRespone)
        {
            var okCount = 0;
            port.ReadTimeout = 1000;
            port.WriteTimeout = 1000;
            for (int sendTryCount = 0; sendTryCount < maxTry; sendTryCount++)
            {
                try
                {
                    port.WriteLine(data);
                }
                catch
                {
                    Thread.Sleep(50);
                    continue;
                }
                Thread.Sleep(2);
                for (int receiveTryCount = 0; receiveTryCount < 3; receiveTryCount++)
                {
                    try
                    {
                        var ch = port.ReadChar();
                        if (ch == okRespone)
                        {
                            okCount++;
                            if (okCount >= minTry)
                            {
                                return okCount;
                            }
                        }
                        break;
                    }
                    catch
                    {
                        Thread.Sleep(50);
                        continue;
                    }
                }
                if (port.BytesToRead > 0)
                {
                    var buffer = new byte[port.BytesToRead];
                    port.Read(buffer, 0, buffer.Length);
                }
            }
            return okCount;
        }

من در شبیه ساز پورت دستگاه رو در این حد پیاده سازی کردم :
کد:
            do
            {
                try
                {
                    string s = _port.ReadTo("\r\n");
                    _port.Write("K");
                }
                catch
                {
                }
            } while (true);

و کد شما قاعدتا باید شبیه به این از متد استفاده کنه :
کد:
            comPort.Open();
            if (SendTextToPort(comPort, something, 1, 20, 'Y') != 1)
            {
                // تلاش ناموفق برای برقراری ارتباط اولیه
                // تعداد 20 بار تلاش انجام شد ولی حتی یک کاراکتر
                // Y
                // دریافت نشد
            }
            else
            {
                // ارتباط اولیه برقرار شد و دستگاه یک کاراکتر
                // Y
                // را برگرداند
                var successfull = true;
                for (int i = 0; i < programData.Count; i++)
                {
                    var data = string.Format("p{0}{1}={2}b{3}", paramStart, i, programData[i], Environment.NewLine);
                    if (SendTextToPort(comPort, data, 3, 20, 'K') != 3)
                    {
                        // برای ارسال داده شماره
                        // i
                        // به تعداد 20 تلاش صورت گرفت اما متاسفانه 3 پاسخ
                        // K
                        // دریافت نشد
                        successfull = false;
                        break;
                    }
                }
                if (successfull)
                {
                    // تمامی داده ها با موفقیت ارسال شدند
                }
            }
            comPort.Close();
 

pb559blue

Active Member
من واقعا از کمک شما ممنونم.
دوتا مسئله: اول اینکه چرا سرعت اجرا اینقدر کنده؟ هر بار ارسال من حدود 1 ثانیه طول میکشه. چطور میتونم این سرعت رو بالا ببرم.
و دوم اینکه هر بار اجرای کد باعث متوقف شدن اجرای رویداد‌های دیگه فرم میشه. برای مثال در مدت زمانی که من منتطر دریافت کاراکتر Y هستم، امکان حرکت دادن فرم رو روی دستکاپ و جابجا کردنش ندارم تا وقتی که یا Y رو دریافت کنم، یا شرط برقرار نشه و ارسال به دستگاه رو متوقف کنم.
این مورد وقتی شروع به ارسال رشته‌های اصلی میکنم خیلی اذیت کننده است. چون تعداد خونه‌های آرایه حدود 100 تاست و با توجه به وقفه 1 ثانیه‌ای زمان زیادی طول میکشه تا یا پیغام ارسال نشدم داده رو دریافت کنم یا داده‌ها تماماً ارسال بشن.

این رو هم بگم که ظاهرا سرعت اجرای کد روی دستگاه 50 میکرو ثانیه است. و کد روی دستگاه توی یک حلقه Do...Loop بینهایت نوشته شده و البته با VB

با این حال واقعا از کمک شما تا این لحظه خیلی ممنونم. من همش تصور این بود که همیشه به رویداد DataReceived نیاز دارم.
 

the_king

مدیرکل انجمن
من واقعا از کمک شما ممنونم.
دوتا مسئله: اول اینکه چرا سرعت اجرا اینقدر کنده؟ هر بار ارسال من حدود 1 ثانیه طول میکشه. چطور میتونم این سرعت رو بالا ببرم.
سرعت پایین به دو دلیل می تونه باشه. دلیل عمده احتمالا بخاطر مکث های Sleep ئه، چون پورت که همون پورت ئه و تغییر نکرده. اول اون سه سطر Thread.Sleep داخل SendTextToPort رو حذف کنید یا با // از کار بندازید.
اگر کافی نبود دو تا سطر port.ReadTimeout و port.WriteTimeout اول کد اش نوشته ام که بعید می دونم کم کردنشون لازم باشه ولی می توانید روی مقدار کمتری مثل 100 قرارشون دهید. ولی خیلی کم شون نکنید چون ممکنه ارتباط سالم هم بخاطر Timeout متوقف بشه.

و دوم اینکه هر بار اجرای کد باعث متوقف شدن اجرای رویداد‌های دیگه فرم میشه. برای مثال در مدت زمانی که من منتطر دریافت کاراکتر Y هستم، امکان حرکت دادن فرم رو روی دستکاپ و جابجا کردنش ندارم تا وقتی که یا Y رو دریافت کنم، یا شرط برقرار نشه و ارسال به دستگاه رو متوقف کنم.
این مورد وقتی شروع به ارسال رشته‌های اصلی میکنم خیلی اذیت کننده است. چون تعداد خونه‌های آرایه حدود 100 تاست و با توجه به وقفه 1 ثانیه‌ای زمان زیادی طول میکشه تا یا پیغام ارسال نشدم داده رو دریافت کنم یا داده‌ها تماماً ارسال بشن.

این رو هم بگم که ظاهرا سرعت اجرای کد روی دستگاه 50 میکرو ثانیه است. و کد روی دستگاه توی یک حلقه Do...Loop بینهایت نوشته شده و البته با VB

با این حال واقعا از کمک شما تا این لحظه خیلی ممنونم. من همش تصور این بود که همیشه به رویداد DataReceived نیاز دارم.

شخصا در موارد مشابه تا جایی که بتونم از BackgroundWorker استفاده می کنم. حتی برای تست کدی که برای شما نوشته بودم هم طبق عادت از BackgroundWorker استفاده کردم.
BackgroundWorker برای کار شما یک Thread جدا می سازه تا مستقل از Thread ای که فرم و دکمه ها و رخداد هاشون رو اداره می کنه اجرا بشه. اینطوری وقتی شما با فرم کار می کنید اون در یک Thread دیگه و بدون تداخل پشت پرده کارش رو انجام میده.
اگر خواستید در مورد نحوه استفاده از BackgroundWorker در فروم توضیحات بیشتر هست ولی کلا کد شما همچین حالتی پیدا می کنه که هر جا لازمه کاری پشت پرده و بدون مزاحمت انجام بشه یک کمپوننت BackgroundWorker بکشید و بندازید زیر فرم
و کدی که قراره پشت پرده انجام بشه رو در رخداد DoWork اش بنویسید و کاری که بعد از تموم شدن تمامی کارهای پشت پرده انجام بشه رو در رخداد RunWorkerCompleted اش بنویسید و هر زمانی که خواستید در حین کار پشت پرده مثلا Progressbar ای رو
جلو ببرید از رخداد ProgressChanged اش استفاده می کنید. چون شما با Invoke آشنا هستید شاید لازم تون نشه ولی کلا اگه خواستید در حین انجام کار پشت پرده کاری روی فرم انجام بدید اون رخداد ProgressChanged نیازی به Invoke نداره چون با Thread خود فرم اجرا میشه،
رخداد RunWorkerCompleted هم همینطور. فقط و فقط اون رخداد DoWork ئه که Thread اش جدا است و Invoke ممکنه لازمتون بشه.

اول کاری می کنم که BackgroundWorker بتواند در حین کارش گزارش پیشرفت بدهد، چون بصورت پیشفرض false است و نمی تواند از رخداد ProgressChanged استفاده کند :
کد:
            backgroundWorker1.WorkerReportsProgress = true;

بعد کاری که قراره پشت پرده انجام بشه رو در رخداد DoWork اش می نویسم :
کد:
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            comPort.Open();
.
.
.
            comPort.Close();
        }

توجه داشته باشید که داخل DoWork برای دسترسی مستقیم به کنترل های روی فرم ممکنه Invoke لازمتون بشه.

اجرا کردن این رخداد DoWork می تونه بدون آرگومان باشه :
کد:
        private void button1_Click(object sender, EventArgs e)
        {
            button1.Enabled = false;
            backgroundWorker1.RunWorkerAsync();
        }

اما می توانید موقع اجرا کردن BackgroundWorker آرگومانی رو بهش بفرستید و هر چی متغیر لازم داره داخلش بفرستید تا داخل DoWork از e.Argument برش دارید و نیازی به Invoke نشه :
کد:
        private void button1_Click(object sender, EventArgs e)
        {
            button1.Enabled = false;
            backgroundWorker1.RunWorkerAsync(new object[] { textBox1.Text, textBox2.Text });
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            var args = (object[])e.Argument;
            var text1 = (string)args[0];
            var text2 = (string)args[1];
.
.
.

داخل DoWork در هر لحظه ای می توانید Progress رو گزارش بدید، یک عدد که اصولا درصد ئه ولی لازم نیست حتما درصد باشه و سپس بصورت اختیاری یک مقدار :
کد:
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 99; i++)
            {
                backgroundWorker1.ReportProgress(i);
            }
            backgroundWorker1.ReportProgress(100, "Finish");

این گزارش در رخداد ProgressChanged پردازش میشه و می توانید درصد رو در ProgressPercentage و مقدار اختیاری رو در UserState ببینید و به کنترل های روی فرم هم دسترسی کامل دارید :
کد:
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
            if (e.UserState != null)
            {
                label1.Text = (string)e.UserState;
            }
        }

وقتی کار پشت پرده تمام شد و در واقع کد DoWork به پایانش رسید رخداد RunWorkerCompleted اجرا میشه :
کد:
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            button1.Enabled = true;
        }

می توانید در DoWork داخل e.Result مقداری قرار بدید تا معلوم بشه موفقیت آمیز بوده یا نه. در RunWorkerCompleted هم به اون e.Result دسترسی دارید.
کد:
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            .
            .
            .
            e.Result = successfull;
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if ((bool) e.Result)
            {
                MessageBox.Show("The operation finished successfully");
            }
 

pb559blue

Active Member
من Sleep ها رو غیر فعال کردم.
میزان timeout ها هم در صورتی که بیشتر از 9 میلی‌ثانیه باشه ارسال رشته‌ها انجام نمیشه. در واقع من ارسال میکنم، چراغ ارسال روی usb روشن میشه و با زمانی که گذاشتم همخونی داره. و این یعنی دستگاه پیغامی به من نمیده.
البته این مشکل میلی‌ثانیه‌ها برای ارسال اولیه و دریافت Y نیست. اون بخش تحت هر شرایطی کار میکنه. فقط بخش ارسال رشته‌ها. شاید کد سمت دستگاه باید راه دیگه‌ای برای اطمینان از درستی دریافت‌هاش پیدا کنه. یا شاید هم اصلا به این چک کردن سه‌باره هر رشته اصلا نیازی نباشه.

به نظر شما، ممکنه ایراد از کدنویسی دستگاه باشه؟

نهایتاً من با HyperTerminal و برنامه‌های مشابه جواب میگیرم اما روی دستگاه نه!
مرسی از کمکی که کردی و چیز‌هایی که یادم دادی.
 
آخرین ویرایش:

the_king

مدیرکل انجمن
من Sleep ها رو غیر فعال کردم.
میزان timeout ها هم در صورتی که بیشتر از 9 میلی‌ثانیه باشه ارسال رشته‌ها انجام نمیشه. در واقع من ارسال میکنم، چراغ ارسال روی usb روشن میشه و با زمانی که گذاشتم همخونی داره. و این یعنی دستگاه پیغامی به من نمیده.
البته این مشکل میلی‌ثانیه‌ها برای ارسال اولیه و دریافت Y نیست. اون بخش تحت هر شرایطی کار میکنه. فقط بخش ارسال رشته‌ها. شاید کد سمت دستگاه باید راه دیگه‌ای برای اطمینان از درستی دریافت‌هاش پیدا کنه. یا شاید هم اصلا به این چک کردن سه‌باره هر رشته اصلا نیازی نباشه.

به نظر شما، ممکنه ایراد از کدنویسی دستگاه باشه؟

نهایتاً من با HyperTerminal و برنامه‌های مشابه جواب میگیرم اما روی دستگاه نه!
مرسی از کمکی که کردی و چیز‌هایی که یادم دادی.

Timeout کلا ارسال یا دریافتی رو کند نمیکنه، صرفا برای اینه که مشخص کنه طرف مقابل حداکثر چقدر وقت واکنش داره، اگر بذارید روی 2 میلی ثانیه، دستگاه اگه تو 2 میلی ثانیه کل رشته تون رو دریافت نکنه با خطا کار تموم میشه، دیگه کاری نداره که در حال گرفتن بود یا ارتباط قطع بود یا مشغول
پردازش قبلی بود. Timeout رو تا حدی کم کنید که مطمئن اید ارسال یا دریافت بزرگترین داده قطعا کمتر از اون طول می کشه.

ببینید که در HyperTerminal تنظیمات Port چیه، منظورم Baud Rate و Parity و Stop Bits و ... ئه.

چون از روال دستگاه اطلاعی ندارم نمی تونم نظری بدم که ایراد از کجا است.
 

pb559blue

Active Member
تنظیمات پورت رو تو HyperTerminal انجام میدم. از اون نظر‌ها مشکلی نیست.

اما کد دستگاه رو این آقا طوری نوشته که مثلا:
وقتی من روی دکمه ارسال کلیک میکنم و پارامتر اول رو میفرستم اون قبل از اینکه شروع به دریافت و بررسی رشته بکنه، توی کد وقفه می‌ندازه تا روی دستگاهش سه تا بوق بزنه!

منطق کد دستگاه اینه که: م رشته‌ام رو تو هر ثانیه 500 بار بفرستم و تا وقتی هم که اون کاراکتر K رو به من نداده به همین کارم ادامه بدم. روی پورت کام.
خوب سئوال من اینه که این به نظر شما منطق درستیه. با توجه به اینکه ارسال من هیچوقت قطع نمیشه یا حتی متوقف!؟ و حالا من وقفه نمیتونم تو کدم بدم چون اگه وقفه‌ام بیشتر از 9 میلی‌ثانیه باشه دستگاه روند دریافت داده‌ها رو شروع نمیکنه؟
 

the_king

مدیرکل انجمن
تنظیمات پورت رو تو HyperTerminal انجام میدم. از اون نظر‌ها مشکلی نیست.

اما کد دستگاه رو این آقا طوری نوشته که مثلا:
وقتی من روی دکمه ارسال کلیک میکنم و پارامتر اول رو میفرستم اون قبل از اینکه شروع به دریافت و بررسی رشته بکنه، توی کد وقفه می‌ندازه تا روی دستگاهش سه تا بوق بزنه!

منطق کد دستگاه اینه که: م رشته‌ام رو تو هر ثانیه 500 بار بفرستم و تا وقتی هم که اون کاراکتر K رو به من نداده به همین کارم ادامه بدم. روی پورت کام.
خوب سئوال من اینه که این به نظر شما منطق درستیه. با توجه به اینکه ارسال من هیچوقت قطع نمیشه یا حتی متوقف!؟ و حالا من وقفه نمیتونم تو کدم بدم چون اگه وقفه‌ام بیشتر از 9 میلی‌ثانیه باشه دستگاه روند دریافت داده‌ها رو شروع نمیکنه؟

منطق دستگاه که غیر عادی هست، ولی به هر حال برنامه شما باید باهاش کنار بیاد. اینم می توانید امتحان کنید، شما می توانید اول 500 تا رشته رو بهم بچسبونید و بعد با یک WriteTimeout زیاد یکجا ارسال اش کنید. اینطوری از طرف برنامه شما هیچ وقفه ای در کار نخواهد بود
و با حداکثر سرعت ممکن ارسال می کنه. حتی منتظر پاسخ دستگاه نمی مونه. بعد که ارسال تموم شد هی کاراکتر به کاراکتر از پورت بخوانید ببینید دستگاه چند تا K ارسال کرده.
 

جدیدترین ارسال ها

بالا