// საინჟინრო ჩანაწერები და ტექნიკური გადაწყვეტილებები
საინჟინრო ჟურნალი
ინსპექციის მიტკების წანაცვლების პრობლემის მოგვარება PDF ნახაზების მასშტაბირებისას
პრობლემა: Velder-ისთვის ნახაზების მარკირების ხელსაწყოს შექმნისას, დავამატეთ ტექნიკურ სქემებზე ნიშნულების დასმის ფუნქცია. თუმცა, PDF გეგმის მასშტაბირებისას (pinch zoom) კოორდინატები ინაცვლებდა, რის გამოც ნიშნულები „ცურავდნენ“ თავდაპირველი პოზიციებიდან.
მიზეზი: საწყისი კოდი მასშტაბირების წერტილს სტატიკურად აფიქსირებდა (ეკრანის მარცხენა ზედა კუთხეში). მასშტაბის ცვლილებისას კოორდინატთა ბადე იზრდებოდა, მაგრამ ეკრანის სქროლი არ კორექტირდებოდა მომხმარებლის თითების ჟესტის ცენტრის პროპორციულად, რის გამოც ფონური ნახაზი ინაცვლებდა.
გადაწყვეტა:
- დავიწყეთ მომხმარებლის ორი თითის კოორდინატების თვალყურის დევნება ჟესტის დასაწყისში ზუსტი ფოკუსირების წერტილის (pinch focal point) გამოსათვლელად.
-
გამოვიყენეთ დინამიკური
transform-originცვლილება ჟესტის დროს ნახაზის მიმდინარე ჩარჩოსთან მიმართებაში. -
თითების გაშვებისას, სანამ დროებითი CSS-ტრანსფორმაცია აქტიურია, ვაფიქსირებთ ფოკუსურ
კოეფიციენტებს (
focalRatioX,focalRatioY), ვუშვებთ გვერდის ხელახალ რენდერინგს PDF.js-ის მეშვეობით ახალ მასშტაბში და მყისიერად ვაკომპენსირებთ გადაადგილებასwindow.scrollBy-ის დახმარებით.
შედეგი: ნახაზი და ნიშნულები რჩებიან იდეალურად დაფიქსირებული თითების ქვეშ ნებისმიერი მასშტაბირებისას, რაც უზრუნველყოფს მონაცემთა 100%-იან სიზუსტეს საავადმყოფოს გეგმებზე.
// ფოკუსური კოეფიციენტების ფიქსაცია დროებითი CSS-ტრანსფორმაციის განულებამდე
var cRect = canvas.getBoundingClientRect();
var focalRatioX = cRect.width > 0 ? (pinchFocalClientX - cRect.left) / cRect.width : 0.5;
var focalRatioY = cRect.height > 0 ? (pinchFocalClientY - cRect.top) / cRect.height : 0.5;
renderPage().then(function() {
wrap.style.transform = '';
wrap.style.transformOrigin = 'top left';
// ვასწორებთ ეკრანის სქროლს ისე, რომ ფოკუსური წერტილი დარჩეს ეკრანის იმავე ადგილას
requestAnimationFrame(function() {
var newRect = canvas.getBoundingClientRect();
var newFocalX = newRect.left + focalRatioX * newRect.width;
var newFocalY = newRect.top + focalRatioY * newRect.height;
window.scrollBy(newFocalX - pinchFocalClientX, newFocalY - pinchFocalClientY);
});
});
ხმოვანი NLP-პარსერი დავალებების სწრაფი შექმნისთვის (hands-free)
პრობლემა: Velder-ის ტექნიკოსები ახორციელებენ სამედიცინო აირების ქსელების მონტაჟსა და ტექნიკურ მომსახურებას საავადმყოფოს ზონებში (საოპერაციო ბლოკები, SOR, სამანქანო განყოფილებები). ვინაიდან ისინი მუშაობენ დამცავი ნიღბებითა და სქელი სამუშაო ხელთათმანებით, სენსორულ ეკრანზე ანგარიშების ხელით შეყვანა უკიდურესად მოუხერხებელია. გარდა ამისა, რკინაბეტონის სარდაფები სრულად ბლოკავს მობილურ კავშირს, რაც ართულებს მონაცემთა ხელით შეყვანას.
შეზღუდვა: საავადმყოფოების სარდაფები და ბუნკერები არის ზონები მობილური კავშირის გარეშე. ღრუბლოვანი NLP სერვისების (როგორიცაა Dialogflow, OpenAI ან Google Cloud NLP) გამოყენება შეუძლებელი იყო ინტერნეტის ხშირი შეფერხებების გამო.
გადაწყვეტა: ჩვენ შევქმენით მცირე ზომის ლოკალური ბუნებრივი ენის დამუშავების (NLP) პარსერი პოლონური ენისთვის, რომელიც მუშაობს პირდაპირ მოწყობილობაზე. მას შემდეგ რაც ჩაშენებული Speech-to-Text აქცევს ხმას ტექსტად, ჩვენი პარსერი:
- ამოიცნობს ფარდობით თარიღებს (მაგალითად, "jutro" [ხვალ], "dzisiaj" [დღეს], კვირის დღეებს, რიგობით რიცხვებს, როგორიცაა "pierwszego" [პირველ რიცხვს]).
- გამოყოფს დროს (მაგალითად, "rano" [დილას], "wieczorem" [საღამოს], "o 14" [14:00 საათზე]).
- ასუფთავებს წინადადებას დამხმარე სიტყვებისგან, გამოყოფს დავალების არსს და ავტომატურად გეგმავს მას ლოკალურ ბაზაში.
შედეგი: ტექნიკოსს შეუძლია თქვას: "przypomnij mi jutro o 10 o teście szczelności" (შემახსენე ხვალ 10 საათზე ჰერმეტულობის ტესტის შესახებ) და აპლიკაცია სრულიად ოფლაინ რეჟიმში, ავტომატურად შექმნის დავალებას: "teście szczelności" ხვალისთვის 10:00 საათზე.
// თარიღების დამუშავების გამარტივებული ლოგიკა პოლონური ტექსტიდან მოწყობილობაზე
export function parsePolishDate(input: string): Date {
const now = new Date();
let target = new Date(now);
if (/jutro/i.test(input)) {
target.setDate(now.getDate() + 1);
}
const timeMatch = input.match(/o\s+(\d{1,2})/i);
if (timeMatch) {
target.setHours(parseInt(timeMatch[1], 10), 0, 0, 0);
}
return target;
}
ფონური სინქრონიზაცია და კონფლიქტებისგან თავისუფალი SQLite ტრანზაქციების რიგი
ოფლაინ-ხარჯთაღრიცხვისთვის
პრობლემა: HVAC-ის სამონტაჟო ობიექტები ხშირად მდებარეობს სარდაფებში ან შორს მდებარე გარეუბნებში, სადაც მობილური კავშირი არ არის. ტექნიკოსებს სჭირდებათ ასობით მილის, კოლექტორისა და სერვისის შემცველი ხარჯთაღრიცხვის შედგენა. კავშირის დაკარგვისას აპლიკაცია ითიშებოდა, რაც იწვევდა მონაცემთა დაკარგვას.
გადაწყვეტა: ჩვენ დავნერგეთ offline-first ტრანზაქციების რიგი Expo SQLite-ის ბაზაზე. როდესაც ტექნიკოსი ამატებს მასალებს ოფლაინ რეჟიმში:
-
ცვლილებები იწერება ლოკალურ ბაზაში და სერიალიზდება ცხრილში
offline_mutationsდელტა-მუტაციების სახით. - NetInfo ფონურ რეჟიმში აკონტროლებს ქსელის სტატუსს.
- ქსელის აღდგენისას მენეჯერი თანმიმდევრულად აგზავნის მუტაციებს Firebase Cloud Functions-ში.
შედეგი: მონაცემთა 100%-იანი შენარჩუნება კონფლიქტების ავტომატური მოგვარებით ("ბოლო ჩანაწერი იმარჯვებს" პრინციპით).
// SQLite-დან ოფლაინ დაგროვილი მუტაციების თანმიმდევრული გაგზავნა
async function syncOfflineQueue() {
const mutations = await db.getAllAsync('SELECT * FROM offline_mutations ORDER BY timestamp ASC');
for (const mut of mutations) {
try {
await api.post('/sync-mutation', JSON.parse(mut.payload));
await db.runAsync('DELETE FROM offline_mutations WHERE id = ?', mut.id);
} catch (err) {
if (err.status === 409) {
await resolveConflict(mut);
} else {
break; // retry on next network change
}
}
}
}
ბრენდირებული მრავალგვერდიანი PDF ხარჯთაღრიცხვის გენერაცია სმარტფონზე 1.5 წამში
პრობლემა: მობილურ მოწყობილობებზე დიდი HVAC PDF ხარჯთაღრიცხვის პირდაპირი გენერაცია იწვევდა ინტერფეისის გაჭედვას (UI lag) და მეხსიერების კრაშებს ბიუჯეტურ Android ტელეფონებზე.
გადაწყვეტა:
ჩვენ გადავწერეთ PDF კომპილაციის პროცესი
react-native-html-to-pdf
-ის მეშვეობით იზოლირებულ ნაკადში. გადავიყვანეთ მაღალი რეზოლუციის ლოგოები ოპტიმიზებულ
WebP Base64 ფორმატში, ჩავაშენეთ მსუბუქი Google Fonts სტილების ფაილში და შევქმენით PDF
გვერდების ასინქრონული რენდერინგი.
შედეგი: ოპერატიული მეხსიერების დატვირთვა შემცირდა 70%-ით, ხოლო გენერაციის დრო 6 წამიდან 1.2 წამამდე შემცირდა ყოველგვარი სერვერული რესურსების გარეშე.
// PDF-ის ასინქრონული კომპილაცია ფონურ რეჟიმში
const htmlContent = `
\${renderEstimateTable(estimateData)}
`;
const file = await RNHTMLtoPDF.convert({
html: htmlContent,
fileName: \`estimate_\${estimateId}\`,
directory: 'Documents',
});