Paleidus dešimties tūkstančių skaičiuoklių paketinį konvertavimą per naktį, ryte trys iš jų grįžta su False. Tai visa analizė po įvykio, kurią suteikia loginis įrašymo rezultatas: tik nesėkmių skaičius, be informacijos, kuris failas, kuris lapas ar kuri iš keliolikos galimų priežasčių buvo atsakinga. HotXLS, losLab savasis Delphi ir C++Builder komponentas Excel failams, pakeičia tą vienintelį bitą struktūrine diagnostika. Sąsaja IXLSWorkbookProgress pateikia Diagnostics sąrašą ir OnDiagnostic įvykį, kurie kiekvienam Open, SaveAs ir Recalculate iškvietimui praneša stabilų skaitinį kodą, svarbos lygį, nepavykusią operaciją ir lapą, kuriame tai įvyko
Kodėl loginis įrašymo rezultatas neatlaiko didelio masto?
Vienas nepavykęs failas nėra problema, kurią sukuria loginis rezultatas; tūkstantis tokių failų jau yra problema. Kai SaveAs grąžina ne sėkmę trims failams iš dešimties tūkstančių, kitas klausimas visada toks pats: ar šiuos tris galima bandyti dar kartą, ar reikia žmogaus? Teisių klaida tinklo bendrinamame kataloge nėra tas pats incidentas kaip formulė, kurios skaičiavimo variklis negali įvertinti, ir nė vienas iš jų nėra tas pats kaip darbalapis, tyliai viršijęs formato ribą. Kai turite tik sėkmės ar nesėkmės rezultatą, kiekvienas atvejis tampa vienodu pagalbos tarnybos pranešimu, o kažkas turi rankiniu būdu atidaryti kiekvieną failą programoje Excel ir žiūrėti į jį, kol priežastis taps akivaizdi. Šis rankinis pirminis rūšiavimas yra tikroji loginės API kaina, ir ji didėja tiesiškai pagal paketo dydį, būtent tokia savybė, kurios nenorite iš klaidų apdorojimo
Kas slypi IXLSWorkbookProgress viduje: ką turi TXLSDiagnostic
IXLSWorkbookProgress yra sąsaja, kurią HotXLS naudoja pranešti ir apie operacijos eigą, ir apie tai, kas joje nutiko, o abi pusės dalijasi viena sutartimi ne be priežasties: ilgai trunkantis Open, SaveAs ar Recalculate iškvietimas turi perduoti abu dalykus nesukeldamas išimties operacijos viduryje. Eigos pusę sudaro OnProgress ir OnProgressEx, kurie siunčia etapą, būseną bei dabartinės ir bendros reikšmės porą. Šio straipsnio tema yra diagnostikos pusė: Diagnostics savybė grąžina TXLSDiagnostics sąrašą, LastDiagnostic yra nuoroda į naujausią įrašą, o OnDiagnostic įvykis suveikia tą akimirką, kai sukuriamas kiekvienas TXLSDiagnostic įrašas. Kiekviename įraše yra skaitinis Code, TXLSDiagnosticSeverity, jį sukūrusi TXLSDiagnosticOperation, žmogui skaitomas Message, SheetIndex ir SheetName, taip pat NativeCode, išsaugantis žemesnio lygio grąžinamąją reikšmę, kuri sukėlė įrašą
var
Book: TXLSXWorkbook;
Diag: TXLSDiagnostic;
I: Integer;
begin
Book := TXLSXWorkbook.Create;
try
if Book.SaveAs('quarterly-report.xlsx') <> 1 then
for I := 0 to Book.Diagnostics.Count - 1 do
begin
Diag := Book.Diagnostics[I];
Writeln(Format('[%d] severity=%d sheet="%s": %s',
[Diag.Code, Ord(Diag.Severity), Diag.SheetName, Diag.Message]));
end;
finally
Book.Free;
end;
end;
Taip skaitomas Diagnostics jau pats savaime yra pranašesnis už loginį rezultatą, nes Code ir SheetName paslaptį paverčia konkrečiu, filtruojamu faktu. TXLSDiagnostic įrašas apima daugiau nei išveda šis pavyzdys: RecordId ir StreamOffset skirti baitų lygio analizei BIFF sraute, o PartName saugo OOXML ZIP įrašą, pavyzdžiui, xl/worksheets/sheet3.xml, iš kurio kilo problema. Prieš kuriant aplink juos įrankius verta žinoti: dabartiniame leidime nė viena integruota diagnostikos vieta neužpildo RecordId ar StreamOffset, todėl abi reikšmės lieka jų konstruktoriaus numatytojoje -1 reikšmėje, reiškiančioje "netaikoma", o ne "nulis". Jų nebuvimą laikykite normaliu reiškiniu, o ne savo apdorotuvo klaida
Du varikliai, viena forma, vienas tylus skirtumas
HotXLS pateikia du variklius su tuo pačiu ataskaitų modeliu: BIFF8 fasadą seniems .xls failams ir OOXML fasadą .xlsx failams, tačiau jie IXLSWorkbookProgress pateikia ne visiškai vienodai. TXLSWorkbook, .xls variklis, formaliai įgyvendina IXLSWorkbookProgress, todėl jį galima perduoti visur, kur tikimasi šio sąsajos tipo. TXLSXWorkbook, .xlsx variklis, pateikia tas pačias Diagnostics, LastDiagnostic, OnDiagnostic, OnProgress ir OnProgressEx narius tais pačiais pavadinimais ir tipais, tačiau kaip paprasta klasė, o ne formaliai įgyvendinta ši sąsaja, todėl vienas pats neatitiks IXLSWorkbookProgress parametro. Praktikoje tai retai svarbu, nes dauguma kodų vienu metu dirba su viena konkrečia darbaknygės klase, tačiau tai reiškia, kad negalite parašyti vieno pagal IXLSWorkbookProgress tipizuoto pagalbinio metodo ir nekeičiant perduoti jam bet kurio variklio darbaknygės objekto. Vienintelis lauko skirtumas, tiesiogiai kylantis iš formato, yra PartName: jį užpildo tik XLSX variklis, nes tik OOXML turi įvardijamas ZIP dalis
Kas daro diagnostikos kodą saugiai naudotiną šakojimuisi?
Laukas Code yra vienintelė diagnostikos dalis, su kuria verta užkoduoti palyginimą; Message tam netinka, nes prose yra būtent toks dalykas, kurį vėlesniame leidime galima perrašyti, išversti iš naujo ar papildyti išsamesne informacija, niekam nelaikant to nesuderinamu pakeitimu. Integruoti HotXLS diagnostikos kodai jau rodo, kad šis skirtumas apgalvotas: su įrašymu susiję kodai yra nuo 1000 iki 1005, su atidarymu susiję kodai yra 1100 ir 1101, su skaičiavimu susiję kodai yra 1200 ir 1201, o nepalaikomo formato kodas yra 1300; kiekviename intervale paliktos spragos, užuot numeravus visus kodus iš eilės. Dėl tokio išdėstymo tiekėjas gali pridėti naują įrašymo metu pasitaikančią nesėkmę, tarkime, 1006, nepernumeruodamas kodų, nuo kurių jau priklauso jūsų switch sakinys, todėl prieš įsipareigodami gamyboje tikrinti diagnostikos kodą verta ieškoti tokios savybės bet kurioje diagnostikos API, ne tik šioje. Savo paskirstymo logikoje vis tiek palikite numatytąją šaką, kad ir kokie stabilūs atrodytų numeriai, nes naujų nesėkmių režimų nuolat atranda tobulinamas analizatorius ar rašiklis. NativeCode ir ExceptionClass yra vienu sluoksniu žemiau už Code, kai reikia eskaluoti: NativeCode išsaugo pagrindinę grąžinamąją reikšmę, įskaitant HRESULT iš Structured Storage iškvietimo, o ExceptionClass įrašo Delphi išimties tipą, kai toks buvo, ir to paprastai pakanka tiksliam pagalbos prašymui pateikti nepridedant visos dėklo sekos
Svarba ir operacija nusprendžia, ką jūsų kodas darys toliau
Svarba ir operacija diagnostikos įrašą iš žurnalo eilutės paverčia maršrutizavimo sprendimu. TXLSDiagnosticSeverity apima Info, Warning, Error ir Fatal, o TXLSDiagnosticOperation kiekvieną įrašą susieja su jį sukūrusiu iškvietimu: Open, Save, Calculate arba Export. Šios dvi ašys tyčia nepriklausomos: xlsDiagnosticUnhandledException yra vienas fiksuotas kodas, suveikiantis tada, kai Operation nustatyta į iškvietimą, kuris iš tikrųjų sukėlė išimtį, todėl Code atsako, kas nutiko, o Operation atskirai atsako, kur tai nutiko, nereikalaujant atskiro kodo išimčiai atidarant ir kitam išimčiai įrašant. Dėl šio suderinamumo maršrutizavimas tampa mechaninis: užregistruokite įspėjimą ir tęskite, tipiškas pavyzdys yra per Aborted vėliavėlę atšauktas įrašymas; suskaičiuokite klaidą ir tęskite paketą, tipiškas pavyzdys yra darbalapis, kurio nepavyko serializuoti; sustabdykite paketą esant kritinei svarbai, nes šis lygis reiškia, kad neapdorota išimtis jau išvyniojo iškvietimą, o tęsiant galima dirbti su pusiau atnaujinta būsena. Viena sąžininga išlyga: Info egzistuoja išvardijime kaip numatytoji reikšmė, nuo kurios pradedamas naujas TXLSDiagnostic, tačiau visos diagnostikos vietos, integruotos į šiandieninį HotXLS leidimą, kelia tik Warning, Error arba Fatal; Info palikta ateičiai, o ne variklio šiandien generuojama reikšmė
// same Diagnostics loop as above, routed by severity instead of printed flat:
for I := 0 to Book.Diagnostics.Count - 1 do
begin
Diag := Book.Diagnostics[I];
case Diag.Severity of
xlsDiagnosticWarning:
Writeln(Format('WARN [%d] %s', [Diag.Code, Diag.Message]));
xlsDiagnosticError:
begin
Writeln(Format('ERROR [%d] %s (sheet %s, native %d)',
[Diag.Code, Diag.Message, Diag.SheetName, Diag.NativeCode]));
Inc(FailedSheetCount);
end;
xlsDiagnosticFatal:
raise Exception.CreateFmt('Fatal HotXLS diagnostic %d: %s', [Diag.Code, Diag.Message]);
end;
end;
OnDiagnostic prijungimas prie paketinio proceso
Po kiekvieno iškvietimo tikrinti Diagnostics tinka vienam failui; tai nustoja veikti grįžus prie dešimties tūkstančių failų naktinio paketo, nes Diagnostics išvalomas kiekvieno Open, SaveAs ir Recalculate iškvietimo pradžioje. Perskaitykite jį po trečio failo cikle ir matysite tik trečio failo diagnostiką; tai, ką pranešė pirmi du failai, jau išnyko. OnDiagnostic tai išsprendžia paversdamas rinkinį srautu: užsiprenumeruokite vieną kartą prieš ciklą, o tas pats apdorotuvas bus iškviestas kiekvienam failui eilės tvarka, kai failo vardas išliks pasiekiamas per egzemplioriaus lauką
type
TBatchConverter = class
private
FCurrentFile: string;
FFailedFiles: TStringList;
procedure HandleDiagnostic(Sender: TObject; Diagnostic: TXLSDiagnostic);
end;
procedure TBatchConverter.HandleDiagnostic(Sender: TObject; Diagnostic: TXLSDiagnostic);
begin
if Diagnostic.Severity >= xlsDiagnosticError then
FFailedFiles.Add(Format('%s: [%d] %s (sheet %s)',
[FCurrentFile, Diagnostic.Code, Diagnostic.Message, Diagnostic.SheetName]));
end;
// inside the batch loop:
Book.OnDiagnostic := HandleDiagnostic;
for I := 0 to FileNames.Count - 1 do
begin
FCurrentFile := FileNames[I];
if Book.Open(FCurrentFile) = 1 then
Book.SaveAs(ChangeFileExt(FCurrentFile, '.xlsx'));
end;
Kiek iš tikrųjų kainuoja atgalinis iškvietimas
OnDiagnostic yra nebrangus dėl struktūrinės priežasties: jis suveikia tik tada, kai kažkas jau negerai, o klaidų pasitaiko retai, palyginti su darbaknygėje esančių langelių, eilučių ar darbalapių skaičiumi. Palyginkite tai su OnProgress ir OnProgressEx, kurie praneša įprastą eigą ir nuo pradžių turėjo būti sukurti atsižvelgiant į iškvietimų dažnį. HotXLS praneša apie eigą darbalapio lygiu po vieną kartą kiekvienam lapui per Open ir SaveAs, o ne po kartą kiekvienam langeliui ar eilutei, todėl vieno iškvietimo sąnaudos išlieka mažos net darbaknygėse su milijonais langelių; Recalculate žengia dar toliau ir riboja savo eigos įvykį maždaug iki vieno karto per keturis procentus priklausomybių grafiko, tad visas perskaičiavimas suteikia pulso signalą, o ne užtvindo vartotojo sąsajos giją įvykiais. Diagnostikai tokio ribojimo nereikia, nes įvykių skaičių apriboja faktinių problemų skaičius, o ne failo dydis
Vienintelė vieta, kur našumas vis dar priklauso nuo jūsų, yra pats apdorotuvas. OnDiagnostic suveikia sinchroniškai gijoje, vykdančioje Open, SaveAs ar Recalculate, todėl blokuojantis apdorotuvas, pavyzdžiui, sinchroninis rašymas į nuotolinę žurnalų tarnybą, tampa šio iškvietimo trukmės dalimi. Vienam failui tai nepastebima. Dešimties tūkstančių failų pakete tai yra skirtumas tarp darbo, baigiamo per naktį, ir darbo, kuris dar vykdomas pietų metu, todėl buferiuokite tai, ką apdorotuvas turi atlikti, ir išsiųskite tai asinchroniškai, užuot lėtąją dalį vykdę iškvietimo viduje
Struktūrinė diagnostika vertingiausia ten, kur loginis rezultatas silpniausias, tai yra darbo procesuose, kuriuose apdorojama daug failų, o ne vienas. Aiškiausias pavyzdys yra darbaknygės audito ir konvertavimo procesas: užuot įrašę tik kiekvieno failo sėkmę ar nesėkmę, pridėkite kiekvieno failo Diagnostics sąrašą prie audito įrašo, ir ataskaita parodys ne tik, kas nepavyko, bet ir kodėl; būtent tai pirmiausia siekia tinkamai išspręsti mūsų straipsnis apie darbaknygės audito ir konvertavimo darbo vietos kūrimą. Toks pats eigos ir diagnostikos derinys tinka bet kuriam procesui, kuriam ir taip reikia eigos ataskaitų, būtent apie tokią sritį kalbama mūsų didelių darbaknygių našumo programoje HotXLS vadove, kuriame ilgas Open arba SaveAs iškvietimas yra pakankamai įprastas, kad OnProgress jau būtų prijungtas, o OnDiagnostic būtų natūralus ir beveik nemokamas priedas šalia jo
Niekur šiame procese nereikia įdiegto Excel, taip pat nereikia gaudyti bendrinės išimties ir spėlioti, ką ji reiškė. IXLSWorkbookProgress ir jos Diagnostics, LastDiagnostic bei OnDiagnostic nariai yra standartinio HotXLS Component, skirto Delphi ir C++Builder, dalis kartu su visa diagnostikos kodų nuoroda ir likusia Open, SaveAs bei Recalculate sąsaja, kurią šiame straipsnyje apžvelgėme