Files
Core_Be/scripts/agent-face-photo-contract.md
T
Dhananjaya99 bffc9b1686 first commit
2026-09-28 11:44:26 +05:30

5.9 KiB
Raw Blame History

Agent contract — UpsertFacePhoto

Status: implemented on both sides. The agent side lives in Access-device-agent (Devices/HikvisionIsapiClient.SetUserFaceAsync and the UpsertFacePhoto case in Services/CommandWorker.DispatchAsync). This document remains the reference for the wire format and the device call.

Not yet verified against a physical terminal — see "Untested" at the end.

Flow

Web  ──POST client/customers/{id}/face-photo──▶  Server
                                                  │ validates JPEG, queues DeviceCommand
                                                  ▼
Agent ──GET  api/agent/commands ─────────────────▶ receives UpsertFacePhoto
      ──ISAPI POST────────────────────▶ Hikvision terminal (gym LAN)
      ──POST api/agent/commands/{id}/result ────▶ Server
                                                  │ purges payload, updates profile
                                                  ▼
                                                SignalR "FacePhotoUpdated" ──▶ browser

Command the agent receives

GET api/agent/commands returns the usual envelope; CommandType is UpsertFacePhoto.

PayloadJson:

{
  "bindingId":       "8f1c...",     // guid, AccessDeviceBinding
  "accessProfileId": 6,             // long
  "employeeNo":      "HF6",         // the person id already used by UpsertPerson
  "name":            "Prabodha",
  "faceJpegBase64":  "/9j/4AAQ...", // bare base64, no data: prefix
  "desiredVersion":  4
}

The image is already normalised by the browser and re-checked server-side:

Property Guaranteed
Format JPEG (magic bytes verified)
Dimensions 480×640 from the web client; ≤ 1920×1920 enforced
Size ≤ 200 KB enforced, typically 60–150 KB

So the agent does not need to resize or convert. Decode the base64 and POST it.

Ordering: the person record must exist on the terminal before a face can attach to it. employeeNo is the same value UpsertPerson uses. If a face command arrives for a person the terminal doesn't have, run the person upsert first rather than failing.

ISAPI call

Face data is attached to an existing person in a face library. Typical shape for the DS-K1T series (confirm against your firmware — this varies more than Hikvision's docs suggest):

POST /ISAPI/Intelligent/FDLib/FDSetUp?format=json
Content-Type: multipart/form-data; boundary=---boundary
Authorization: Digest ...

---boundary
Content-Disposition: form-data; name="FaceDataRecord"
Content-Type: application/json

{ "faceLibType": "blackFD", "FDID": "1", "FPID": "HF6", "name": "Prabodha" }
---boundary
Content-Disposition: form-data; name="FaceImage"; filename="face.jpg"
Content-Type: image/jpeg

<binary jpeg>
---boundary--
  • FPID should be the employeeNo, so the face ties to the same person record.
  • Use Digest auth, as with the other endpoints.
  • Replacing a photo is the same call — the device overwrites the FPID's face.

Result the agent posts back

POST api/agent/commands/{id}/result

Success:

{ "success": true, "resultJson": "{\"fpid\":\"HF6\"}" }

The server reads fpid (also accepts FPID, faceId, faceURL) into AccessProfile.FacePhotoFPID — the handle needed to replace or delete the face later. Anything longer than 64 chars is truncated.

Failure:

{ "success": false, "resultJson": "{\"detail\":\"No frontal face detected in the image\"}" }

Put a human-readable reason in detail — it is surfaced to gym staff verbatim, so it should say what to do about it. Real Hikvision rejections worth mapping:

Device response Suggested detail
lowFaceQuality / no face found No clear frontal face was detected. Retake with the face centred and well lit.
face already bound to another FPID This face is already enrolled for another member.
library full The terminal's face library is full.
image format rejected The terminal rejected the image format.

Retry behaviour

MaxAttempts is 3 for face commands (lower than the 5 used for person/state commands): a face rejected for quality will be rejected identically on every retry, so failing fast gets the real message in front of staff sooner.

  • Retries use the existing exponential backoff.
  • On success or final failure the server clears PayloadJson, so the image is not retained. A retry between those points still has the payload available.
  • A newer photo supersedes an older queued one — the stale command is marked Superseded and its payload cleared. The agent should treat Superseded as "stop, discard".

Deleting a face

The agent side is implemented (DeleteUserFaceAsync, plus a DeleteFacePhoto dispatch case), calling:

PUT /ISAPI/Intelligent/FDLib/FDSearch/Delete?format=json&FDID=1&faceLibType=blackFD
{ "FPID": [ { "value": "HF6" } ] }

The server does not queue this command yet — nothing calls it when a member is removed. Adding that is a small change: queue DeleteFacePhoto with the stored FacePhotoFPID wherever a member is deactivated, then clear the face columns on success.

Untested

Everything here compiles and the wire format matches on both sides, but no part of this has run against a physical Hikvision terminal. Expect to verify:

  • FDID = "1" and faceLibType = "blackFD" are the right library on your model. Terminals with multiple face libraries may need a different FDID.
  • The multipart part names (FaceDataRecord, FaceImage) — these vary between firmware generations more than Hikvision's documentation suggests.
  • Whether your model accepts 480×640 at ~150 KB, or wants smaller.

The agent logs the full device response for both success and failure (SetUserFace {EmployeeNo} ... : {Response}), which is the fastest way to see what the terminal actually objects to on the first real run.