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

151 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`:
```json
{
"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:**
```json
{ "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:**
```json
{ "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.