← EKOS presentations · System documentation

TSD: a barcode data-collection terminal and its exchange server

How the application works, recovered only from the compiled binaries in its release folder. The source code, design documents and developers were not consulted.

github.com/alexeyban/tsd · SmartDeviceProject3/SmartDeviceProject2/SmartDeviceProject2/bin/Release · 31 files · 12 managed assemblies · compiled by EKOS 2026-09-18 · extractor ekos-cil-metadata/v2 · zero LLM calls

Summary

TSD (the handheld window is titled “Data Collection Terminal”) is a two-part retail back-office system. Store staff use a Windows CE handheld to scan goods for price checks, stock-taking, receiving trucks and boxes, and returns. A desktop exchange server loads reference data onto the handheld and pulls the scan results back.

  • The two sides share no network connection and no database server. They exchange files over ActiveSync/RAPI while the terminal sits in its cradle. compiled
  • The server imports the product catalogue and documents from windows-1251 text files. It converts them into a custom indexed file database (FamilTsdDB) and copies that database to the terminal. compiled
  • On the terminal, every scan passes through an action engine. The document a product belongs to decides what the handheld does: which sound or vibration plays, and whether a price label goes to a Bluetooth printer. compiled
  • Every change to a scan record is journaled on the terminal to scannedbarcodes.txt and register.txt the moment it happens. The files are copied back to the desktop and archived with a timestamp, and each import's status is emailed. compiled observed
  • Product records carry a NavCode, and the handheld shows it under the label “Navision code”. The upstream ERP is therefore most likely Microsoft Dynamics NAV. reading
12managed assemblies read
888types recovered
8,327methods, with signatures
10,956resolved call edges
174external I/O boundaries
0database I/O boundaries

2How to read this document

Every statement is tagged with how EKOS obtained it. Most statements also carry a binary · token locator: an ECMA-335 metadata token such as TSDClient.exe · 0x0600004B. That token names the exact method in the exact build (binary SHA-256 is recorded on every fact). Any claim can be re-checked with ekos_binary_explain or ekos query object.

compiled read deterministically from IL/metadata: names, signatures, call targets, string and numeric constants observed content of a non-binary file EKOS observed in the folder graph answer of an EKOS graph query (ekos_impact, ekos_dependents, ekos_neighborhood) reading an interpretation that joins several EKOS facts. It is not itself a compiled fact, and the evidence it rests on is stated.

The application's user interface is in Russian. Every on-screen string quoted here is an English translation of a constant compiled into the binaries; the Russian originals can be read with ekos_binary_explain. File names containing the Russian word for “copy” are shown translated.

3What is in the release folder

EKOS detects formats by magic bytes and the PE CLI header, never by file extension. Of the 14 PE files, 13 are managed .NET assemblies and one (MCSSLib.dll, ARM Thumb, Windows CE) is native code. EKOS records the native file as a File and reads nothing from it. compiled

AssemblyFileRoleTypesMethodsCLRUsed by
TSDClientTSDClient.exeHandheld application, window title “Data Collection Terminal”1171,189v2.0entry point
TSDClientTSDClient - Copy.exeSecond copy of the handheld app, a different build (two fewer types)1151,155v2.0
TSDServerTSDServer.exeDesktop exchange server, window title “TSD Exchange Server”59817v2.0entry point
OBReadLibNetobreadlibnet.dllOptical barcode reader SDK wrapper (namespace Calib)7209v1.1TSDClient ×2 graph
BluetoothLibNetbluetoothlibnet.dllBluetooth stack SDK wrapper (Calib)8125v1.1TSDClient ×2 graph
SystemLibnetsystemlibnet.dllDevice system SDK: buzzer, vibrator, events (Calib)5246v1.1TSDClient ×2 graph
ImagerLibNetImagerLibNet.dllCamera imager / OCR SDK (Calib)13244v1.1nothing graph
MoFlinkLibNetMoFlinkLibNet.dllDevice SDK: FLK_* file, status and system-info structures (Calib)18217v1.1nothing graph
OpenNETCFOpenNETCF.dllOpenNETCF Smart Device Framework (third party)3752,591v2.0OpenNETCF.Drawing, .Windows.Forms
OpenNETCF.Windows.FormsOpenNETCF.Windows.Forms.dllOpenNETCF UI controls1321,248v2.0TSDClient ×2
OpenNETCF.DrawingOpenNETCF.Drawing.dllOpenNETCF drawing30197v2.0OpenNETCF.Windows.Forms
ControlsLibcontrolslib.dllCustom controls, namespace Bananamama.BackOffice.TSD.Client989v2.0nothing graph

The CLR column is the metadata version string: v2.0.50727 is .NET Compact Framework 2.0, and v1.1.4322 is the device vendor's older SDK build. "Used by" counts real DependsOn edges resolved onto binaries in this same folder. compiled graph

The Calib namespace, the OBR*/BT*/Sys* API names, and a test-print string naming "CASIO IT-600 or DT-X11" (in BTPrintClass) together suggest the handheld hardware is a Casio industrial terminal. reading

4Architecture

Two executables and a set of device SDK wrappers. The only channel between desktop and handheld is file copy over ActiveSync, through OpenNETCF.Desktop.Communication.RAPI. compiled

Upstream (ERP) product file · document file windows-1251 text fixed-width or delimited Downstream RemoteFilePath archive load-status email (SMTP) KOI8-R, .xls attachment DESKTOP (Windows) TSDServer.exe Form1 · “TSD Exchange Server” DataLoaderClass — import UploadResults — export Program.Main /c — batch mode SendMailClass — SMTP log4net · IPC :9090 FamilTsdDB files {table}.db · .col · .idx · .idf TSDDBPAth Results files scannedbarcodes_{ts}.txt register_{ts}.txt ActiveSync RAPI CopyFileToDevice CopyFileFromDevice SetDeviceTime HANDHELD (Windows CE) TSDClient.exe MainForm menu · 7 workflows ActionsClass — action engine ScanClass · BTPrintClass FamilTsdDB reader Settings.xml OBReadLibNet scanner SystemLibnet buzzer · vibro BluetoothLib + COM port BT printer LABEL_{n}.DEF Terminal storage \Storage Card\tsdfamilia scannedbarcodes.txt (journal) register.txt · *.def
Component and data-flow view. Every box and label is a compiled type, method, string constant or observed file. The upstream and downstream systems (dashed) are outside the binaries; they are known only by the file formats and SMTP calls the server uses.

Shared source, two executables

The handheld app declares its classes in the TSDServer namespace. Both executables contain their own compiled copies of ProductsDataSet, ScannedProductsDataSet and the whole FamilTsdDB storage engine. Server and client were built from one shared code base. compiled

No database server anywhere

EKOS found 0 calls into any database provider (SqlClient, SqlServerCe, OleDb, …) in all 12 binaries. The typed TableAdapters read and write FamilTsdDB files, not SQL. The products.sdf SQL CE connection string in the settings is never used. compiled graph

5The exchange cycle, end to end

A working day for one terminal, reconstructed from the call graph. Steps 1–3 and 6–7 run on the desktop, steps 4–5 on the handheld.

External systems TSDServer.exe ActiveSync / RAPI TSDClient.exe 1 · AutoLoadProduct / AutoLoadDoc 2 · BeginImport → FamilTsdDB files 3 · button2_Click: date check, DeleteDeviceFile, BeginCopyFileToDevice 4 · Program.Main: Settings.xml, DB MainForm → workflows 5 · scan → action → journal line scannedbarcodes.txt · register.txt 6 · downloadBtn_Click: CopyFileFromDevice 7 · UploadResults → {name}_{yyyyMMddhhmmss}.txt File.Copy → RemoteFilePath DeleteOldDB (if EraseTerminalDB): DeleteDeviceFile SendMail: load status · batch timeouts
Every arrow is a compiled call path. The method names are the real names in the binaries. compiled
  1. Import reference data TSDServer.exe · 0x0600000D, 0x06000024

    The operator presses “Import product file” or “Import document file”. The handlers call DataLoaderClass.AutoLoadProduct / AutoLoadDoc, which start the import on a background thread (LoadFile, 0x060002C5). A second import cannot start while one is running: “Load in progress!”. compiled

  2. Parse and store TSDServer.exe · 0x060002CA

    BeginImport clears the target tables (CleanProducts/CleanDocs) and reads the file as windows-1251. It dispatches each line to a fixed-width or delimited parser depending on the ImportFileTypeIsFixed setting (AddFixedStringProducts, AddDelimetedStringDocs, …). Finally it commits through the table adapters' Update. Lines with a bad barcode are skipped with “Invalid barcode - line skipped”. Other errors either abort or continue, depending on BreakOnError. compiled

  3. Upload to the terminal TSDServer.exe · 0x06000011

    The upload handler (“Upload to terminal”) first calls GetDBDate and compares the catalogue date with today. If the catalogue is old, it warns: “Warning: the catalogue … is dated {0}! Are you sure you want to load old data?”. It then connects over RAPI, deletes any existing database files on the device, and streams each file with BeginCopyFileToDevice behind a cancellable progress dialog. When the device connects, ActiveSync_Active also syncs the terminal clock (RAPI.SetDeviceTime). compiled

  4. Terminal start-up TSDClient.exe · 0x06000464

    Program.Main loads Settings.xml next to the executable. If the file is missing, it creates a settings row with built-in defaults: printer 00:03:7a:32:4c:55, COM 9, storage path \Storage Card\tsdfamilia, StorageMemorySize 15000. The main form then shows the numbered menu and starts the scanner (ActionsClass.BeginScan). compiled observed

  5. Scanning see §7–§10

    Staff pick a workflow and scan. Each accepted scan updates the in-memory ScannedBarcodes table. BeginScan 0x06000167 subscribes to that table's RowChanged/ColumnChanged events. Their handlers call WriteDbTxt 0x0600016A, which appends the row to scannedbarcodes.txt as a |-separated line and to register.txt, then flushes. A crash or battery loss therefore does not lose a scan. compiled

  6. Download results TSDServer.exe · 0x06000025

    “Download from terminal” copies the scanned-data files back with RAPI.CopyFileFromDevice. It reports every file that failed to copy. compiled

  7. Publish results TSDServer.exe · 0x060002D5, 0x06000026

    UploadResults writes scannedbarcodes.txt (7 fields, {0}|…|{6}) and register.txt ({0},{1,11:D}, {2,7:D}) into LocalFilePath. It copies each one to RemoteFilePath as {name}_{yyyyMMddhhmmss}.txt. If EraseTerminalDB is set, DeleteOldDB then deletes the scanned files on the terminal. compiled

6Desktop exchange server (TSDServer.exe)

A Windows Forms tray application (notifyIcon1, “TSD Server”) with a status log (richTextBox1). It has two modes, chosen in Program.Main 0x060002B5. compiled

Interactive mode

Opens Form1 and registers an IPC remoting channel at ipc://localhost:9090/RemoteObject.rem that exposes RemoteObject.Show. This is most likely a single-instance mechanism: a second launch asks the running window to show itself. compiled reading

Batch mode: /c

Configures log4net from log4netconfig.xml, runs AutoLoadProduct and then AutoLoadDoc, and waits up to 900,000 ms (15 min) for each. On timeout it logs ERROR LOAD PRODUCT - TIMEOUT / ERROR LOAD DOCS - TIMEOUT, emails AddressToList through SendMailClass, and returns an ERRORLEVELS exit code. It is built to run from a scheduler. In both modes, DataLoaderClass_OnFinishImport 0x060002C7 also emails the import log, subject “Data load status”, after every import. compiled

ComponentTokenWhat it does
Form10x02000003Main window: import buttons, upload/download, ActiveSync connect/disconnect/IP-change handlers, RAPI copy progress, tray icon. 29 methods; most complex is the upload handler (cyclomatic complexity 15).
DataLoaderClass0x0200002FImport pipeline: threaded load, fixed and delimited parsers for products and docs, ParseColumn (complexity 12), catalogue date (GetDBDate, NoDateException), and result export (UploadResults). Raises StartImport/ProcessImport/FinishImport/FailedImport events.
SendMailAttach.SendMailClass0x02000038SMTP sender using the default network credentials, KOI8-R body encoding and an application/vnd.ms-excel attachment. Also usable stand-alone: its usage text is SendMailAttach Subject Body To Attachment_FileName.
Compressor0x02000037GZip plus BinaryFormatter helpers. No method in any binary calls them. graph
SettingsForm0x0200001CImport format editor: column widths, field delimiter, file type (fixed/delimited), date format and separator, decimal separator, database path, and the program path on the terminal.
Properties.Settings0x02000039Persisted server settings. See §14.

7Handheld application (TSDClient.exe)

A .NET Compact Framework 2.0 Windows Forms application driven entirely from the keypad. Menus are numbered and hot keys are announced on screen, for example “Enter – Yes, FN+CLR – No” and “F4 - Results”. compiled

KeyMain-menu item MainForm · 0x0200001DOpens
1Price checkViewProductForm
2Stock-takingInventarForm
3Internal box receivingIncomeForm
4Truck receivingTtnForm
5SettingsSettingsForm
6Delivery notesInventarForm (SimpleIncome mode)
7ReturnsReturnForm
0Exit (only when EnableExit)Application.Exit

The labels are the button captions, translated from Russian compiled. MenuEvents 0x0600013C constructs exactly the forms in the right-hand column compiled. The one-to-one pairing of key and form is inferred from caption order and each form's own title strings, for example TtnForm's title is “Truck receiving”. reading

TypeTokenMethodsResponsibility
ActionsClass0x0200002296Singleton core: action dispatch, document open/close, scan journal, lookups, sounds, vibration, label printing. Largest type in the app.
ViewProductForm0x0200000E18Product screen used by several workflows. Holds the two most complex methods in the system (§16).
BTPrintClass0x0200003846Bluetooth printer discovery, pairing, serial-port I/O, reconnect and print queue.
ScanClass0x0200002F13Barcode reader lifecycle on a background thread; raises Scanned.
InventarForm · IncomeForm · TtnForm · ReturnForm10–12One form per workflow (§8). TtnForm hands each truck's boxes to IncomeForm. ReturnBoxForm is unreachable (§16).
ViewBoxForm · ViewInventarForm · ViewTtnForm · ViewDocsForm · ViewLoadDateForm6–8Result and status viewers (F4 screens, data information).
NativeClass0x0200006A15P/Invoke: device ID via KernelIoControl, OEM info, memory division, WCE_PlaySound.
FamilTsdDB.*0x02000054–5E~100Embedded file database engine (§12).

8Business workflows

Each workflow below is described from its form's handlers and the verbatim prompts and messages compiled into them. compiled

1 · Price check ViewProductForm

Scan a product or type its Navision code (“Navision code”). The screen shows name, article, OldPrice/NewPrice (formatted ######.00) and planned vs. actual quantity. F1 / F2 print a label with template 1 or 2 (“F1-Print1”, “F2-Print2”). F4 lists every document the product appears in (ViewDocsForm). A product that is also part of a return is flagged “This item is part of a return”.

2 · Stock-taking InventarForm · 0x060001DC

Start by scanning the “new count sheet barcode”. The count is opened (OpenInv) or resumed (“Continue the count?”). Items are then scanned into it, and F3 closes it (“F3-Close count”). A count that is already closed is refused (“Count … already completed!”). A wrong location barcode gives “Barcode {0}: wrong address!”. The InventarMode enum distinguishes UseReturns / DontUseReturns.

3 · Internal box receiving IncomeForm · 0x02000070

Scan a box barcode. The terminal finds the delivery note it belongs to and shows “Note No. {0}: {1} boxes”. It rejects foreign boxes (“This is a foreign box!”), boxes already accepted, and barcodes that are not box barcodes. F1 accepts a box in full (“F1-accept in full”). Running totals show articles and pieces planned vs. accepted until “BOX FULLY ACCEPTED”.

4 · Truck receiving by waybill (TTN) TtnForm · 0x02000031

Scan the TTN (consignment note) barcode. A TTN for another store is rejected (“Waybill is not for this store!”), as is a closed one (“Waybill already closed!”). A TTN moves through WAYBILL TO ACCEPT → WAYBILL BEING RECEIVED → WAYBILL ACCEPTED, per CheckStatus. Its boxes are then received box by box (CarScanMode: CarsScan / BoxScan). ViewTtnForm shows the truck as a tree of boxes with accepted/total counts.

6 · Delivery notes InventarForm, SimpleIncome mode

The same form as stock-taking, in InventarFormMode.SimpleIncome. Enter or scan a delivery-note number (“Enter note No.”). Start or continue it, scan items against the plan, then close it (“Do you want to close the note?”).

7 · Returns ReturnForm

Scan a product. The terminal lists every open return that includes it (FindAllReturnsByProduct), or shows “No returns found”. Returned goods are packed into return boxes (CreateNewReturnBox, CheckOpenedReturnBox, CloseReturnBoxAction). A box that is already closed is refused: “Box {0} already closed”. A dedicated ReturnBoxForm exists in the binary, but nothing opens it (§16).

5 · Settings SettingsForm · 0x0600000D

1: find and pair the Bluetooth printer (SearchPrinterForm). 2: clear scanned data, with a two-step confirmation (“After clearing, all previously scanned data will be unavailable!”). This deletes scannedbarcodes.txt and register.txt. 3: data information (ViewLoadDateForm): last load date and a database health check (TestDB, CheckDbFile).

Quantity correction

Two dialogs, ChangeQtyForm and MultiplyForm, gated by EnableChgQty (read by the result viewers) and EnableChgMlt (read by the product screen). They validate input (“Quantity out of range”, “Invalid format”). In the product screen, undoing the last scan asks for confirmation (“Reduce code {0} … from quantity {0} to quantity {1}?”) and refuses to go below zero (“Cannot reduce any further!”).

9The action engine

A scan's outcome is not hard-coded per screen. ActionsClass.InvokeAction(ActionCode, ProductsTblRow, DocsTblRow) 0x0600018D dispatches through an ActOnProduct delegate to one handler per action code. The document row carries per-document behaviour in LabelCode, MusicCode, VibroCode and Priority. So the back office, by preparing the document file, decides what the terminal does when an item is scanned. compiled reading

TSDUtils.ActionCode members graphDisplay labels, translated (ActionCodeDescription) compiled
NoAction · Returns · Reprice · Remove · InventoryGlobal · QuickHelp · InventoryLocal · IncomeBox · CloseInventar · Cars · SimpleIncome · BoxWProducts · CarsBoxes · CloseIncome · BoxIncomes · BoxReturns · ReturnBoxWProducts · CloseBoxReturns · ReturnsTTN · ReturnInInventory · StrangeBox · AlreadyAccepted · NotFound · DocNotFound NO ACTION · RETURN · REPRICING · TRANSFER · Inventory, global · QUICK HELP · Inventory, spot check · Box receiving · Close global inventory · Deliveries (waybill) · Goods receiving · Notes and boxes · Trucks and boxes · Close note · Notes · Return boxes and goods · Return notes and boxes · Close return box · Return notes · Item not found · Document not found

Enum members come from ekos_neighborhood on the ActionCode type. Labels are the string constants of ActionCodeDescription 0x02000007, in declaration order. They pair up closely: Returns/RETURN, Reprice/REPRICING, Remove/TRANSFER, QuickHelp/QUICK HELP. EKOS does not read enum constant values, so the exact numeric mapping is not asserted.

HandlerTokenBehaviour
RepriceActionProc0x0600017BFinds the scan row. Plays the document's sound and vibration, prints a label using the document's LabelCode template (ActionCodeDescription.GetShablonPrintLabel), and increments FactQuantity.
SimpleIncomeActionProc · InventoryGlobalActionProc · InventoryLocalActionProc0x0600017F · 0x0600017EAdds or increments the scan row for (barcode, DocType, DocId) with the document's plan quantity and priority, then signals completion.
BoxWProductsActionProc · AcceptFullBoxWProductsActionProc · IncomeBoxAction · IncomeCarBoxAction0x06000181 · 0x06000182Box and truck receiving: per-item or whole-box acceptance.
ReturnActionProc · ReturnBoxWProductsActionProc · CloseReturnBoxAction0x06000180 · 0x060001B1Returns and return boxes.
RemoveActionProc · NoActionProcTransfer; no action.
NotFoundActionProc · DocNotFoundActionProc0x06000184Unknown product or document: warning sound and vibration only.
UndoLastScannedPosition0x0600019CDecrements FactQuantity of the last scan.

Sound and vibration 0x0600016F · 0x06000172

PlaySound(code) reads SOUND_{code}.def next to the executable, parses each line into a SoundDef (type, frequency, time) and drives Calib.SystemLibNet.Api.SysPlayBuzzer. Vibration does the same with VIBRO_{code}.def. The folder ships codes 10, 12 and 250 for both. Their observed contents document the SysPlayBuzzer/SysPlayVibrator parameters. compiled observed

Label templates 0x06000174 · 0x06000178

PrintLabel reads LABEL_{n}.DEF, replaces placeholders with ReplaceAttr, and sends the result to the printer. The placeholders are <GOODS_ATTRIBUTE_x>, <DOCS_ATTRIBUTE_x>, <SCAN_ATTRIBUTE_x> and <SYSTEMDATE>; ReplaceAttr's numeric constants 60 and 62 are the character codes of < and >. Values are formatted by type (dd.MM.yyyy, ######.00) and encoded with CustomEncodingClass, a Cyrillic code page embedded as a string table. On a busy printer it retries, up to WaitPrintTimeDefault. No LABEL_*.DEF file is present in this release folder. compiled graph

10Anatomy of one scan

The path from trigger press to journal line, following resolved Calls edges. compiled

  1. Reader thread waits ScanClass.InitScan 0x060001FF · start 0x06000200

    InitScan configures the reader through OBReadLibNet: symbologies, scan key, buffer type, notification, buzzer and vibrator. It then calls OBROpen and starts a thread. The thread blocks on SysWaitForEvent("OBRScanningEvent", 2000), reads the text, and raises Scanned. Supported symbologies include Code39, NW-7, UPC/EAN, ITF, Code93, Code128, MSI, IATA and EAN-13/GTIN.

  2. Screen receives it ViewProductForm.Scanned 0x06000049

    The active form's handler (OnScanned / Scanned) looks up the product (SearchBarcode, GetProductRowByNavCode) and the document (GetDataByDocIdAndType, FindByDocIdAndDocType).

  3. Rules are applied ViewProductForm.DoAction 0x0600004B

    DoAction (complexity 38, 33 branches) decides between the outcomes whose messages it carries: “Item already received: {0} of {1}”, “Receive {0} more pcs”, “Item is not in this box!”, “DEFECTIVE”, “Stock-taking mode”, “Delivery-note mode”. It then calls ActionsClass.InvokeAction.

  4. Scan is recorded ActionsClass.AddScannedRow 0x060001A4

    AddScannedRow finds or creates the row keyed by (Barcode, DocType, DocId) and calls AcceptChanges. The table's change events fire WriteDbTxt 0x0600016A, which appends an 8-field line ({0}|…|{7}, UTF-8) to the journal and a line to register.txt. Opening and closing documents, boxes and trucks write their own marker lines directly (OpenInv, CloseInv, CloseDoc, CloseCarAction, CreateNewReturnBox, CloseReturnBoxAction, IncomeBoxAction).

  5. Feedback Action_OnActionCompleted 0x06000053

    The ActionCompleted event updates planned vs. actual counts on screen. It plays the document's sound, and when a box is complete it shows “Box on note … fully received!”.

Blast radius: ekos_impact on AddScannedRow, following Calls in reverse, finds 29 methods that write scan records through it: 14 direct callers, and the deepest 4 hops away (the query bound was 5, so this is the complete set). Among them are DoAction, navCodeTB_KeyDown, OpenInv/CloseInv, every *ActionProc, CheckStatus, OnIncomeScanned and CreateNewReturnBox. Any change to the scan-record key or format has to be checked against all of them. graph (raw result)

11Data model

Typed ADO.NET DataSets, recovered from their generated row accessors (get_X/IsXNull) and schema strings. compiled

ProductsTbl

ProductsDataSet · client + server

Barcode · NavCode · Article · ProductName · Country · Structure · OldPrice · NewPrice · MarketPrice · Message · Message2 · AcceptDefect

MarketPrice, Message2 and AcceptDefect exist only in the client build; the server writes the other 9 columns.

DocsTbl

ProductsDataSet · client + server

DocId · DocType · NavCode · DocumentDate · Quantity · Priority · LabelCode · MusicCode · VibroCode · Text1 · Text2 · Text3

One row per (document, product): planned quantity plus the per-document print, sound and vibration behaviour.

ScannedBarcodes

ScannedProductsDataSet · client + server

Barcode · DocId · DocType · PlanQuanity · FactQuantity · ScannedDate · TerminalId · Priority

The result of the day's work. Primary lookup is (Barcode, DocType, DocId). "PlanQuanity" is misspelled in the schema itself.

TypedSettings / UntypedSettings

SettingsDataSet · client

19 typed columns (§14) plus a free-form Name/Value table.

ProductsBinTbl · DocsBinTbl · DocsBinTbl1

ProductsDataSet · server only

ProductsBinTbl's column set is combined with FieldsLength in DataLoaderClass.SetFormats 0x060002C2 to define the fixed-width import layout. DocsBinTbl/DocsBinTbl1 (adding Shablon, RePriceDate and ReturnDate) have no reader in the call graph.

DocumentClass

TSDClient · 0x02000065

View model rendered as “Document {0} No. {1} dated {2} … Plan: {6}, Actual: {7}”.

12Storage engine and file formats

FamilTsdDB is an in-house embedded database compiled into both executables. It stores each DataTable as a set of files, and the typed TableAdapters (BaseTableAdapter: Open/Fill/Update/Close) sit on top of it. compiled

{dir}\{table}.colColumn definitions, one per line ({0}|{1}|{2}|{3}|{4}|{5}), taken from the DataTable's name, type, max length, nullability and uniqueness (DataTable..ctor 0x06000419).
{dir}\{table}.dbRows as binary records (BinaryWriter). They are read back by seeking and BitConverter.ToUInt16-prefixed reads (Fill 0x06000426, Write 0x0600041C).
{dir}\{table}_{col}.idx / .idfPrimary and secondary indexes with hashed IndexItems. Lookups: FindByPk, FindByIndex, FindByIndexes, FindAllByPartIndexes (prefix match), plus direct-scan and reverse-scan fallbacks. The most complex method in the engine is the index iterator FindIndexes (complexity 17).
Text fileWritten byFormatExample (observed)
scannedbarcodes.txt (terminal journal)TSDClient ActionsClass8 fields, |, UTF-86600000000001|6600000000001|8|0|02.01.2003|0|0|0
scannedbarcodes.txt (desktop export)TSDServer UploadResults7 fields, |2900655000005|02: 15.06.11|2|1|29.06.2011|1|0
register.txtboth{0},{1,11:D}, {2,7:D}2896459000009, 1, 1

Field order is read from the getter call order. UploadResults exports Barcode, DocId, DocType, FactQuantity, ScannedDate, TerminalId, Priority. The terminal's WriteDbTxt journals Barcode, DocId, DocType, FactQuantity, ScannedDate, TerminalId, Priority, PlanQuanity. Both observed samples fit. compiled observed reading The journal's first line uses the document's own barcode as both Barcode and DocId, which is the open-document marker OpenInv writes. Its 660… prefix matches the "660" constant in InventarForm, so stock-taking count sheets most likely use a 660 barcode range. reading OpenScanned 0x06000163 rebuilds the in-memory table from the journal whenever a workflow is opened from the menu or an open document or box is looked up; its numeric constants 10 and 124 are '\n' and '|'. compiled

13Device integration

Barcode reader

ScanClass → Calib.OBReadLibNet.Api

OBRLoadConfigFile, OBRSetDefaultSymbology, OBRSetScanningKey, OBRSetScanningNotification, OBROpen, OBRClearBuff, OBRSaveConfigFile. Scanning can be paused and resumed around dialogs (PauseScan/ResumeScan). Errors reported: "Failed to connect to the scanner." and "Trigger keys are being used…". compiled

Bluetooth label printer

BTPrintClass → Calib.BluetoothLibNet.Api + serial port

BTInitializeBTInquiry (device search) → BTRegisterDeviceInfoBTSetDefaultDeviceBTConnectSerial (BTPORT_SERIAL). It then opens COM{n}: from BTComPort and writes through SerialPort. The connect method's numeric constant 19200 is most likely the baud rate reading. A string in the same method reads "Extech BT printer found!". Every step is logged to BTLog.txt, and a lost connection opens BTConnectionErrorForm with reconnect. compiled

Buzzer and vibrator

ActionsClass → Calib.SystemLibNet.Api

SysPlayBuzzer and the vibrator API, parameterised by the .def files. SoundCodes members: B_CLICK, B_TAP, B_WARNING, B_ALARM, B_SCANEND, B_WIREREAD, B_CARDREAD, B_USERDEF. compiled graph

Device identity

NativeClass.GetDeviceID · 0x06000471

GetDeviceID can read the hardware ID with KernelIoControl (formatted {0:X8}-{1:X4}-{2:X4}-{3:X4}-…), but nothing calls it. The TerminalID stamped into scan rows and exported results comes from Settings.xml, so two terminals with the same settings file report the same ID. compiled graph

14Configuration reference

Handheld: Settings.xml (SettingsDataSet.TypedSettings)

SettingMeaning (from where it is used)Observed value
TerminalIDStamped on scan rows and exported results
DatabaseStoragePathFamilTsdDB directory used by TestDB/CheckDbFile\Storage Card\tsdfamilia
BTPrinterAddress · BTComPortPaired printer MAC; virtual COM port for the printer00:03:7a:32:4c:55 · 9
WaitPrintTimeDefaultPrint retry wait in PrintLabel
DefaultRepriceShablon · BlueButtonShablonLabel templates used by the product screen's print keys (both read in navCodeTB_KeyDown)
StorageMemorySizeStorage/program memory split, applied at start-up: Program.MainNativeClass.SystemStorageMemorySetSystemMemoryDivision15000
EnableExit · EnableWorkWOPrinterAllow exit from the menu; allow the product screen to work without a printer (read in ViewProductForm_Load)
EnableChgQty · EnableChgMltAllow quantity edit (read by the result viewers) / multiply (read by the product screen)
BaseDateDate baseline, read by the table adapters and ActionsClass2000-01-01
DatabaseFileName · ProductsConnectionString · VibarationOn · ScanSoundNotifyOn · ComBufferSize · CurrentDateNever read. Their getters have no incoming Calls edge from any method in any binary. They are written as defaults and persisted, but nothing acts on them. This includes the SQL CE database name and connection string. graphproducts.sdf · Data Source=|DataDirectory|\Products.sdf · false · false · — · —

Setting names and defaults are compiled into TypedSettingsDataTable and Program.Main compiled. "Read by" is the set of direct callers of each getter, from ekos_impact over resolved Calls edges graph. Values come from the observed Settings.xml excerpt observed. "—" means the excerpt EKOS stored was truncated before that element.

Desktop: TSDServer.Properties.Settings

LocalFilePath · RemoteFilePath · TSDDBPAth · EraseTerminalDB · ImportProductsEnabled · ImportDocsEnabled · ImportFileTypeIsFixed · FieldsLength · FieldDelimeter · DateSeparator · ShortDatePattern · NumberDecimalSeparator · BaseDate · BreakOnError · SettingsEnabled · SmtpClient · AddressFrom · UserNameFrom · AddressToList

15Release-folder file reference

FileWhat it isEvidence
TSDClient.exe · TSDServer.exeThe two applicationscompiled
TSDClient - Copy.exe · TSDClient.ex_Copies of the handheld app. The .ex_ is recorded as a file only; the binary observer does not pick it up.compiled
TSDClient.pdbDebug symbols (650 KB). EKOS does not read PDBs.observed
*.dll (11)See §3. MCSSLib.dll is native ARM code.compiled
Settings.xml · _Settings.xml · Settings - Copy.xmlTerminal settings plus two variantsobserved
SOUND_10/12/250.def · VIBRO_10/12/250.defBuzzer and vibrator patterns per MusicCode/VibroCodeobserved compiled
scannedbarcodes.txt · scannedbarcodes.txt1 · register.txtSample results: a desktop-format export (2011 dates) and a terminal journal (2003 dates)observed
BTLog.txtBluetooth printer log. Its 1/3/2003 timestamps suggest the device clock was unset.observed
tsdfamilia.zip877-byte archive; contents not observedobserved
enkaTCdaily.xmlAn SQL Server Reporting Services export (reportserver, "JANUS Head Office"). Unrelated to TSD and not referenced by any binary.observed graph

16Hotspots, risks and anomalies

MethodBinary · tokenComplexityWhy it matters
ViewProductForm.navCodeTB_KeyDownclient · 0x0600005142Every key on the product screen: print, close count, close note, undo, add quantity
ViewProductForm.DoActionclient · 0x0600004B38The per-scan business rules (§10)
ViewBoxForm.RefreshDataclient · 0x0600044324Box result listing
IncomeForm.textBox1_KeyDownclient · 0x0600049222Box receiving keys. An identical copy sits in the unreachable ReturnBoxForm (0x06000156).
BTPrintClass.TestPrintclient21Printer diagnostics
ActionsClass.ReplaceAttrclient · 0x0600017818Label template substitution
DataLoaderClass.BeginImportserver · 0x060002CA15All inbound data passes through here

Duplicated binaries

Three builds of the handheld app sit side by side (.exe, - Copy.exe, .ex_) with different type counts, plus three settings files. Which one is deployed cannot be told from the folder. compiled

Missing label templates

PrintLabel requires LABEL_{n}.DEF, and no such file is in this folder. Printing depends on files deployed some other way. compiled graph

Dead weight shipped

ImagerLibNet, MoFlinkLibNet and ControlsLib have no dependents. ControlsLib itself references NetworkLib and TsdCommandsLib, which are not in the folder. Its NewForm uses a NetMan command/reply protocol, a trace of a different, networked client. graph compiled

God class

ActionsClass holds 96 methods: dispatch, persistence, lookups, device feedback and printing. It is the one type that nearly every workflow calls, and 29 methods reach its scan-record writer (§10). compiled graph

Hard-coded defaults

A specific printer MAC (00:03:7a:32:4c:55), COM port 9 and \Storage Card paths are compiled into Program.Main as fall-backs. A terminal without Settings.xml silently targets that one printer. compiled

Dead settings

Six of the terminal's 19 typed settings are never read. They include both feedback switches (VibarationOn, ScanSoundNotifyOn) and the SQL CE DatabaseFileName/ProductsConnectionString. Turning feedback off in Settings.xml changes nothing: sound and vibration follow the document's MusicCode/VibroCode instead. graph reading

Unreachable code

ReturnBoxForm (a near-copy of IncomeForm, 11 methods) is never constructed, so the screen cannot be opened. Also never called: NativeClass.GetDeviceID, the server's Compressor, and the server's DocsBinTbl/DocsBinTbl1 tables. graph

Plain-text results

Scan results travel and are archived as unencrypted text files, and are emailed with default network credentials. Nothing sensitive appears in the samples, but there is no integrity check on the exchange. compiled reading

EKOS's own Risk objects are all concentration risks on framework assemblies: mscorlib (12 dependents), System.Windows.Forms (8), System, System.Drawing (6), System.Data (5), System.Xml (4). For a .NET Compact Framework 2.0 application, they mostly say that the platform itself is the migration risk. graph

17What EKOS could not determine

  • Exact conditions. EKOS reads binaries at structural fidelity: names, signatures, calls, constants and branch counts, but no reconstructed statements. It knows DoAction has 33 branches and which messages it can show, but not the precise predicate that selects each message.
  • Enum values. Enum members are recovered but not their numeric constants, so the ActionCode numbers behind document DocTypes (such as 2, 5 and 8 in the samples) are not mapped.
  • Import file layouts. Column order and widths are runtime settings (FieldsLength, FieldDelimeter) that are not in the folder.
  • Long call lists. A method's stored call_targets property keeps at most 32 distinct targets (Program.Main has more), with no truncation marker. Every "never called" and "read by" statement here therefore comes from resolved Calls edges through ekos_impact, which are complete for code inside these binaries.
  • Three files. MCSSLib.dll (native ARM), TSDClient.pdb and tsdfamilia.zip are recorded but not read.
  • No LLM stage was run. EKOS's opt-in [binary-reconstruction] stage, which proposes business rules scored for confidence, was not used; only local models were available. Every reading in this document is a human-auditable join of compiled facts, not model output.

18Reproduce and query this analysis

  1. Compile the folder

    git clone --filter=blob:none --sparse https://github.com/alexeyban/tsd.git
    cd tsd && git sparse-checkout set SmartDeviceProject3/SmartDeviceProject2/SmartDeviceProject2/bin/Release
    ekos init
    ekos build && ekos recover && ekos resolve --force && ekos compile && ekos commit
    # recover: 12 binaries, 888 types, 8327 methods, 7134 fields, 174 I/O boundaries
    #          10956 call edges resolved
    # resolve --force: one known false conflict (the "System" assembly vs. a field named "system")
  2. Generate EKOS's deterministic documentation

    ekos docs generate --layout curated               # README / Architecture / API + 9,283 entity pages
    ekos docs generate --layout solution-architect    # risk report, onboarding guide, findings memo

    A copy of the top-level generated reports is kept in examples/tsd/generated/.

  3. Ask EKOS through MCP

    ekos mcp serve --workspace .
    # ekos_ekl             FIND Object WHERE kind CONTAINS 'BinaryType' AND name = 'TSDServer.ViewProductForm'
    # ekos_binary_explain  {id}  → members, complexity, constants, call targets, I/O, provenance
    # ekos_impact          {id, direction: "dependents", kinds: ["Calls"]}
    # ekos_dependents      {id of BluetoothLibNet}  → TSDClient ×2

    The ekos_binary_explain output behind this document is kept for 22 key types in examples/tsd/binary-explain/.