9 Replies Latest reply on Apr 25, 2014 2:54 PM by skwan

    PIServer.CreatePIPoints fails when regional settings set to Russian

    seneschal

      Hi,

       

      I am supporting a Russian customer, and it appears that PIServer.CreatePITags method fails when I have my Windows Regional Settings set to Russian. The method succeeds when the regional settings are set to English.

       

      I admit, I have some non-standard characters in the PI tags I am trying to create - I will give an example - but they odd part is that when I am in English settings, those same odd characters work just fine in creating PI Tags.

       

      Here is an example of a Pi Tag name I am trying to create (which is half garbage I know, but I am doing my best working with some Cyrillic input: 

       
      "rus5_comA_01_sta_T9_id_àá 1 Uáàò1_ccbm__phase_N"
      

       You'll note the non-standard characters in there, these are Unicode characters translated to ASCII. They do not look nice, but this always worked in the past (pre-AF) when using PI-API/PI-SDK. Now, for some reason, this works for me, but not my Russian customer.

       

      It is worthwhile to note that I examined the generated strings (Russian and English) in a hex editor and they are exactly the same. It is also worthwhile to note that I can create the tag using Point Builder in SMT, when I copy this string in there. The problem seems localized to CreatePITags, with Windows set to Russian locale.

       

      I am working on a way to get better tag names out of the Cyrillic characters, but this needs to work in the meantime.

       

      Thanks,

       

      Dave

        • Re: PIServer.CreatePIPoints fails when regional settings set to Russian
          mhamel

          @Dave L: Can you tell the collation in use for the PIFD database on that the SQL Server end? Are the client, server and database computer configured with the same locale? As a final question, which version of AF SDK are you using?

            • Re: PIServer.CreatePIPoints fails when regional settings set to Russian
              seneschal

              Thanks.

               

              I have my dev machine (client) on one box, and the AF Server and SQL Server together on another. The PI Server is on a 3rd. I tried all 3 boxes' regional settings to Russian but I have the same issue.

               

              You'll have to help me out with the collation stuff, I am not familiar with how to find that.

               

              The closest thing I can find to a version is in Control Panel/Programs and Features: "PI AF Client (x64) 2012 with a version of 2.5.1.5159

               

              Sorry forgot to mention earlier - the error I get is: 

               
              [-10551] Invalid Characters in Tag
              

               

               

              Thanks

                • Re: PIServer.CreatePIPoints fails when regional settings set to Russian
                  mhamel

                  @Dave L: If you execute the query below on your SQL Server hosting the PIFD database, you would get the answer.

                   

                   

                   
                  SELECT name, collation_name FROM sys.databases WHERE name = 'PIFD'
                  

                   Can you post the result of your query in this thread?

                    • Re: PIServer.CreatePIPoints fails when regional settings set to Russian
                      seneschal

                      Collation name is - SQL_Latin1_General_CP1_CI_AS

                       

                      I don't know if it matters, but I switched the box back to English regional settings, since having Russian settings did not help.

                        • Re: PIServer.CreatePIPoints fails when regional settings set to Russian
                          seneschal

                          @Ryan

                           

                          This is very interesting.

                           

                          The first thing to note is that the default code page on my box is 1252, even though my locale settings are set to Russian. So I remove the first check from your method.

                           

                          Secondly due to working with some older Russian equipment, I have to support reading files written with CP866 as well as CP1251. In this case the file is CP1251.

                           

                          The method you posted sort of translates these odd characters back to English, I did not expect that. So if I input 

                           
                          rus7_comA_01_sta_T9_id_àá 1 Uáàò1_ccbm__phase_N
                          

                           it changes it to  

                           
                          rus7_comA_01_sta_T9_id_AA 1 Uaao1_ccbm__phase_N
                          

                           Some other characters result in ? characters, but largely convert 

                           
                          rus7_comA_07_sta_T9_id_àá 1 Iáàò íîðì_ccbm__phase_N
                          rus7_comA_07_sta_T9_id_AA 1 Iaao ii?i_ccbm__phase_N
                          

                           Was this the intent of this code?

                           

                          If I read in the file using the correct codepage (1251) and get the Cyrillic characters - then this method has no effect on the string - Cyrillic stays Cyrillic. 

                           
                          rus7_comA_01_sta_T9_id_АБ 1 Uбат1_ccbm__phase_N
                          rus7_comA_01_sta_T9_id_АБ 1 Uбат1_ccbm__phase_N
                          

                           Question marks are still invalid characters for PI Tags though.

                           

                           

                           

                          Thanks,

                           

                          Dave

                            • Re: PIServer.CreatePIPoints fails when regional settings set to Russian
                              rgilbert

                              I looked into Microsoft's routine for converting .NET strings to Multi-byte, and it is a small inline function in msclr::interop::details. This is the line it uses for the conversion from .NET managed strings to C/C++ ANSI strings:

                               
                              size_t _written = ::WideCharToMultiByte(CP_THREAD_ACP, WC_NO_BEST_FIT_CHARS, _pinned_ptr, _str->Length, _buf, static_cast<int>(_size) ,NULL,NULL);
                              

                               The first parameter reveals that the ANSI code page associated with the current thread is used for the conversion to multi-byte:

                              CP_THREAD_ACP

                              Windows 2000: The Windows ANSI code page for the current thread.

                              Note  This value can be different on different computers, even on the same network. It can be changed on the same computer, leading to stored data becoming irrecoverably corrupted. This value is only intended for temporary use and permanent storage should use UTF-16 or UTF-8 if possible.

                               That might explain why you get different results on your en-US machines versus your ru-RU ones.

                               

                               

                               

                              You could try changing the thread's culture info before creating the tag names, and then reverting it back afterwards:

                               
                                          Thread currentThread = Thread.CurrentThread;
                              
                                          // Save current thread culture information
                                          CultureInfo previousInfo = currentThread.CurrentCulture;
                                          CultureInfo previousUIInfo = currentThread.CurrentUICulture;
                              
                                          // Change to Invariant culture
                                          currentThread.CurrentCulture = CultureInfo.InvariantCulture;
                                          currentThread.CurrentUICulture = CultureInfo.InvariantCulture;
                              
                                          // Perform operation
                                          // Your code goes here...
                              
                                          // Revert culture back after tag creation
                                          currentThread.CurrentCulture = previousInfo;
                                          currentThread.CurrentUICulture = previousUIInfo;
                              

                               You would not want to leave it in Invariant culture, because it would affect the way dates, numbers, etc. are displayed.

                               

                               

                    • Re: PIServer.CreatePIPoints fails when regional settings set to Russian
                      rgilbert

                      Can you try and run your tag name through this on one of the machines in the Russian locale? Also, what is the value of System.Text.Encoding.Default.CodePage on the Russian machine.

                       
                              static string ToCyrillicMultiByte(string input)
                              {
                                  int CYRILLIC_CODE_PAGE = 1251;
                                  int WESTERN_EUROPE_CODE_PAGE = 1252;
                      
                                  if (Encoding.Default.CodePage != WESTERN_EUROPE_CODE_PAGE)
                                  {
                                      Encoding cyrillicEncoding = Encoding.GetEncoding(CYRILLIC_CODE_PAGE);
                      
                                      byte[] unicodeBytes = UnicodeEncoding.Unicode.GetBytes(input);
                                      byte[] cyrillicBytes = Encoding.Convert(UnicodeEncoding.Unicode, cyrillicEncoding, unicodeBytes);
                                      input = cyrillicEncoding.GetString(cyrillicBytes);
                                  }
                                  return input;
                              }