Skomplikowany rekord wariantowy

0

Poszukuje rozwiązania takiego problemu: Aplikacja odczytuje poprzez usb dane (cały rekord) z różnych urządzeń. Urządzenia mają dane ułożone w takich samych rekordach rekordach ale z polami o różnej wielkości. Jak utworzyć rekord wariantowy aby był uniwersalny.
Aplikacja powinna wczytywać dane wprost do wybranego wariantu struktury. Problem w tym że pola tej struktury muszą być uniwersalne.

Przykład:
__ Urządzenie 1.__

TU1 = record
  Nazwa   : char;
  Program : byte[10];
  Status  : byte;
end;

Urządzenie 2.

TU2 = record
  Nazwa   : char;
  Program : byte[20];
  Status  : byte;
end;

Oczekiwane rozwiązanie odwołania się do rekordu wyglądałoby mniej więcej tak:

var
  typ: Byte;
begin
  typ:=1;

  TU[typ].Nazwa      := "U1";
  TU[typ].Program[2] := 1;
  TU[typ].Status     := $A;

Wiem że tak się nie da dlatego proszę was o podpowiedź rozwiązania dla tego problemu.

dodanie znaczników <code class="delphi"> i sformatowanie kodu - @furious programming

0

Wariantowy może być tylko początek, więc użyj sobie TU2 tylko dorób funkcję normalizacji dla urządzenia 2: Status:=Program[10];

0

Gdyby ten rekord był taki prosty jak w przykładzie to bym pewnie tak zrobił. Problem w tym że rekord zawiera kilkadziesiąt pól różnych typów. A w tych polach tylko niektóre mają różne rozmiary.
Oczywiście rozważałem zrobienie jednego rekordu uniwersalnego (z maksymalną wielkością pól), osobnych rekordów dla każdego urządzenia i przepisywanie danych w funkcji normalizującej ale bardzo to jest złożone i mało eleganckie. Może jakieś inne pomysły?

0

Tablica struktur z podanymi rozmiarami
Dla podanego przykładu:

const LengthList:array[0..1] of record ProgramSize:Integer; end = ((ProgramSize:10),(ProgramSize:20));
0

Zrób to przez rzutowanie wskaźnika odpowiednio na ^TU1 albo na ^TU2.

0

Wskaźnik musiałby być typu TU1 lub TU2 więc to nic nie da.

0

Co to są za urządzenia i do czego te dane później są wykorzystywane?

0

Są to dane konfiguracyjne wraz z programem odczytywane z różnych sterowników PLC.

1

Nie bardzo widzę, gdzie leży problem. Bo jeśli oczekujesz, że będziesz miał jeden typ danych dla różnych struktur, który będzie Ci się "automagicznie" poprawnie uzupełniał to tak to nie działa. Rekord wariantowy działa tak, że dane do niego są "pakowane" od początku i tak samo odczytywane. Np. Twoje dwa rekordy wyglądają w pamięci tak:

TU1 = record
  Nazwa    : char;
  Program : byte[10];
  Status    : byte;
end;

[N][P][P][P][P][P][P][P][P][P][P][S]

TU2 = record
  Nazwa    : char;
  Program : byte[20];
  Status    : byte;
end;

[N][P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][S]

Teraz jeśli zrobisz z nich rekord wariantowy to zawsze w pamięci będzie zajmował 22 bajty.
[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]

i jak wpiszesz do niego krótszy rekord to dostaniesz
[N][P][P][P][P][P][P][P][P][P][P][S][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
i przy próbie odczytania go jako rekord dłuższy dostaniesz po prostu błędne dane.

To tak w skrócie i nie do końca prawda (dochodzi wyrównanie pól w rekordzie np.).

Poza tym nie wiem jak to ma rozwiązać problem z różnymi ramkami z różnych urządzeń. Przy rekordzie wariantowym i tak musisz podać inne nazwy pól więc cały system bierze w łeb.

Wg mnie idealnie do tego nadają się klasy i dziedziczenie - klasa bazowa z wszystkimi wymaganymi/możliwymi polami oraz konstruktor z parametrem w postaci nieobrobionej ramki (np. Pointer + rozmiar) i klasy z niej dziedziczące, które w konstruktorze miały by (każda własną) metodę rozkodowywania ramki.

0

Niestety wiem jak działają rekordy wariantowe i wiem że tego nie da się w tak prosty sposób zrobić.

Klasy niestety też odpadają ponieważ struktury muszą mieć jedną przestrzeń w pamięci po to aby łatwo można było wysłać całą zawartość (bajt po bajcie) a klasa nie stanowi jedności w pamięci. Wymagałoby to wysyłania pola po polu co byłoby bardzo nieporęczne.

2
Paweł N napisał(a):

Klasy niestety też odpadają ponieważ struktury muszą mieć jedną przestrzeń w pamięci po to aby łatwo można było wysłać całą zawartość (bajt po bajcie) a klasa nie stanowi jedności w pamięci. Wymagałoby to wysyłania pola po polu co byłoby bardzo nieporęczne.

Aż dziw bierze, że tacy "programiści" biorą się za pisanie komercyjnych programów... A nie wpadłeś na to aby wzorem konstruktora z mojego poprzedniego postu (btw. to wcale nie musi być parametr konstruktora ale np. metoda lub właściwość) dodać metodę wirtualną, np. pakuj, którą klasa dla danego sterownika będzie miała zrobioną po swojemu i "spakuje" wszystkie dane z poszczególnych pól w jeden ciąg w pamięci?

Jeśli to ma być w obie strony to aż się prosi aby to było coś w stylu

type 
TByteArr = array of Byte;
TBasePLCData = class
  private

  protected
    procedure SetRawData(const Value: TByteArr); virtual; abstract;
    function GetRawData: TByteArr; virtual; abstract;
  public
    property RawData: TByteArr read GetRawData write SetRawData;

    property Pole1: typ1;
    property Pole2: typ2;
    property Pole3: typ3;
  end;

i każda klasa dziedzicząca implementuje metody GetRawData i SetRawData po swojemu

1

Wymagałoby to wysyłania pola po polu co byłoby bardzo nieporęczne.

Bez przesady - zawsze możesz stworzyć publiczną metodę, która zwróci blok pamięci, uzupełniony w wymagane dane; Mniej byś się użerał, a efektywność wcale nie była by mała - wszystko zależy od implementacji.

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