Ciągły odczyt pliku tekstowego rzuca wyjątkami o byciu w użyciu przez inny proces

0

Witam.
Napisałem pomniejszy programik do niszowego oprogramowania (KCPos), który wyświetla dane z pliku tekstowego. Oprogramowanie oryginalne, zapisuje dane do pliku ekran.txt, a ja je mam czytać i wyświetlić na drugim, małym monitorze. Zasada "działania" jest jak w sklepach Biedronka - kasjerka kasuje, a klientowi pokazuje się na małym wyświetlaczu co jest już skasowane i jaka jest suma.

Kasjerka kasuje na KCPos -> to zostaje wpisane w odpowiednim formacie do pliku ekran.txt -> ja odczytuje i wyświetlam w swoim programie.

PROBLEM
Aby to działało w miarę poprawnie, muszę w funkcji odczytującej linijka po linijce zrobić await Task.Delay(500), choć w niektórych przypadkach to i tak jest za mało. Wygląda to tak jakby kasjerka za szybko kasowała 😂

        public async Task<Document> ReadEkranFile()
        {
            await Task.Delay(500); // tymczasowe rozwiązanie
            Document document = new Document();
            var lines = File.OpenRead(_file.FullName);
            using (StreamReader sr = new StreamReader(lines, Encoding.GetEncoding("Windows-1250")))
            {
                List<Product> products = new List<Product>();
                sr.ReadLine();
                sr.ReadLine();
                while (sr.Peek() != -1)
                {
                    var line = sr.ReadLine();
                    if (line != "-------------------------------------------------------------------------")
                    {
                        if (line.Length >= lineLength)
                        {
                            if (line.StartsWith("STORNO"))
                            {
                                int newLpSize = lpSize + 2;
                                int newNameSize = nameSize - 2;
                                var lp = line.Substring(0, newLpSize).Trim();
                                var name = line.Substring(newLpSize, newNameSize).Trim();
                                var unit = line.Substring(newLpSize + newNameSize, unitSize).Trim();
                                var quantity = line.Substring(newLpSize + newNameSize + unitSize, quantitySize).Trim();
                                var price = "-" + line.Substring(newLpSize + newNameSize + unitSize + quantitySize, priceSize).Trim();
                                var value = "-" + line.Substring(newLpSize + newNameSize + unitSize + quantitySize + priceSize, valueSize).Trim();

                                var product = new Product(lp, name, unit, quantity, price, value);
                                products.Add(product);
                            }
                            else
                            {
                                var lp = line.Substring(0, lpSize).Trim();
                                if (!string.IsNullOrEmpty(lp))
                                {
                                    var name = line.Substring(lpSize, nameSize).Trim();
                                    var unit = line.Substring(lpSize + nameSize, unitSize).Trim();
                                    var quantity = line.Substring(lpSize + nameSize + unitSize, quantitySize).Trim();
                                    var price = line.Substring(lpSize + nameSize + unitSize + quantitySize, priceSize).Trim();
                                    var value = line.Substring(lpSize + nameSize + unitSize + quantitySize + priceSize, valueSize).Trim();

                                    var product = new Product(lp, name, unit, quantity, price, value);
                                    products.Add(product);
                                }
                            }
                        }
                        else if (line.Contains("DO ZAPŁATY"))
                        {
                            int index = line.IndexOf(":") + 1;
                            var toPay = line.Substring(index, 13).Trim();
                            document.ToPay = toPay;
                        }
                        else if (line.Contains("WPŁACONO"))
                        {
                            int index = line.IndexOf(":") + 1;
                            var payed = line.Substring(index, 13).Trim();
                            document.Payed = payed;
                        }
                        else if (line.Contains("RESZTA"))
                        {
                            int index = line.IndexOf(":") + 1;
                            var change = line.Substring(index, 13).Trim();
                            document.Change = change;
                        }
                    }
                }
                document.Products = products;
            }
            counter++;
            return document;
        }

FILE WATCHER

FileSystemWatcher watcher = new FileSystemWatcher(path);
watcher.Filter = "ekran.txt";
watcher.Changed += Watcher_Changed;
watcher.EnableRaisingEvents = true;

WATCHER CHANGED

        private void Watcher_Changed(object sender, FileSystemEventArgs e)
        {
            try
            {
                if (e.Name == "ekran.txt")
                {
                    if (InvokeRequired)
                    {
                        Invoke(new Action(() => LoadDocument(e)));
                        return;
                    }
                }
                else
                {
                    Logger.Info(string.Format("Zmiana w pliku {0}", e.Name));
                }
            }
            catch(Exception ex)
            {
                Logger.Error(ex);
            }
        }

Ja niczego do pliku nie dopisuje, ja go tylko odczytuje, aby wyświetlić co jest nabite na kasę. Nie chce co chwilę dokładać więcej milisekund, bo to nie jest rozwiązanie.

PS.
Z File.ReadLines jest to samo.

0
  1. Nie wiedziałem, ze to takie "zaawansowane" API (plik w katalogu)

  2. Na surowym WIn32 API pewnie można otworzyć plik ze specyficznymi flagami, może to być bardziej odporne na współbieżność (ale nie musi).
    Wysokopoziomowe uniwersalnie funkcje z .NET Frameworka zawsze będą pod tym wzgledem mniej elastyczne.

  3. Nie wiem, czy pomyślałeś (a to jest zw z 1.) ale przy jakiejś dawce szczęścia /pecha ty otworzysz, ale wyjątek poleci tam. Np raz na milion operacji.

1

Umknęło mi, czy nie zamykasz FileStream lines ?

1

Jeśli ma to odbywać się na pliku i ten plik jest nieduży to może przed działaniem zrobić jego kopię, potem odczytać dane i usunąć skopiowany plik.
Będzie można usunąć te 500ms oczekiwania i przy okazji zamknie to możliwość wysypywania błędów w innej aplikacji (pkt 3 z postu @ZrobieDobrze)

1

Jest (nie testowałem) rozwiązanie. Ale z tego wynika, że zapis musiałby być też odpowiednio napisany (FileShare).
https://varunvns.wordpress.com/2012/05/05/reading-and-writing-in-text-files-with-multiple-programs-accessing-it-simultaneously/

0

Na tę chwilę propozycja od @areklipno została zaimplementowana. Obawiałem się, że takie kopiowanie będzie za wolne. Przetestowałem to na swoim komputerze i działa. Teraz klient musi przetestować na sporo wolniejszym POSie czy nie będzie problemu.

@ZrobieDobrze: to jest prehistoryczne oprogramowanie KCFirma. Mój klient jak zobaczył jak wygląda od nich takie "okienko dla klienta" to się za głowę złapał i powiedział, że tego nie wdroży. Gdzie po stronie kasującego to nie musi być ładne, tak po stronie klienta dobrze by było coś sobą reprezentować 😂

0
AdamWox napisał(a):

@ZrobieDobrze: to jest prehistoryczne oprogramowanie KCFirma. Mój klient jak zobaczył jak wygląda od nich takie "okienko dla klienta" to się za głowę złapał i powiedział, że tego nie wdroży. Gdzie po stronie kasującego to nie musi być ładne, tak po stronie klienta dobrze by było coś sobą reprezentować 😂

A może - pytam na zasadzie zachowania wiary w ludzkość - może ten archaiczny protokół czuwający nad plikiem .. może on ma być dzielony i ma realizować lock ?
W onym czasie tym się różnił dobry programista (otwierał pliki int h = open() i dawał flagi do kontroli współużywania) od słabego (fopen i wykluczające otwarcie)

ps. Zagład Energetyczny powinien dać programiście skrzynkę wódki. Tyle lat nap... w kółko procesorów .. ...

0

Może być też, że celowo dali flagę "nie ruszaj", aby nikt nie grzebał i miał problem zrobić swoje rozwiązanie. Nawet samo oprogramowanie nie ma .exe. Uruchamia się z pliku .mde, więc to też świadczy o totalnej prehistorii i ogólnych możliwościach takiego "programu".

0

Teoretycznie możesz podczepić się pod proces i założyć odpowiedniego winapi hooka.

Po co Ci tam Task?

0

Po co Ci tam Task?

Zanim dostałem dostęp do programu, aby to zasymulować, zrobiłem swoje "podmienianie" zawartości pliku na podstawie innych plików i marudził mi o wątkach. I tak to zostawiłem.

Teoretycznie możesz podczepić się pod proces i założyć odpowiedniego winapi hooka.

Wierze, że kopiowanie pliku zda egzanim... Liczę na to.

0
AdamWox napisał(a):

Wierze, że kopiowanie pliku zda egzanim... Liczę na to.

Skąd ta wiara?
Kopiowanie 50ms (strzelam) i potem analiza, vs odczyt z programu 60ms.
We własnym kodzie ma jakies dające się analizować błedy vs przy kopiowaniu nie masz.

Tu nie do końca się zgadzam z wrzucającym tą wersję @areklipno

0

Żeby już mieć problem z głowy, dostać hajs i święty spokój 😂

Ogólnie to jak coś będzie nie tak to głowy mi nie urwą. To wszystko jest po znajomości, więc metoda prób i błędów nie robi żadnego problemu. Jeśli kopiowanie nie zda egzaminu, a pisałem o tym, że to troszkę wolne może być i mam pewne obawy, to pójdę dalej i wybiorę kolejne rozwiązanie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1