All files / server/services/utils pin-validator.ts

96.96% Statements 32/33
95.45% Branches 21/22
100% Functions 11/11
96.42% Lines 27/28

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69                        33x 33x 33x 33x 33x 33x                   107x 107x 107x   107x 3x 6x 6x   3x           104x 45x   104x 48x   104x 5x     3x           101x 101x 42x   101x 1x     100x    
import type { IOPinRecord } from "@shared/schema";
import { pinModeToString } from "@shared/utils/arduino-utils";
 
type PinConflictInfo =
  | { conflict: true; conflictMessage: string }
  | { conflict: false };
 
/**
 * Ensures the pinMode operation is recorded in usedAt and avoids duplicates.
 * Returns true if a new entry was added.
 */
export function ensurePinModeOperation(pin: IOPinRecord, mode: number): boolean {
  const pinModeOp = `pinMode:${mode}`;
  pin.usedAt ??= [];
  const alreadyTracked = pin.usedAt.some((u) => u.operation === pinModeOp);
  Eif (!alreadyTracked) {
    pin.usedAt.push({ line: 0, operation: pinModeOp });
    return true;
  }
  return false;
}
 
/**
 * Evaluates whether a pin has a conflict based on recorded operations.
 * Returns conflict info without mutating the passed pin.
 */
export function computePinConflict(pin: IOPinRecord): PinConflictInfo {
  const ops = pin.usedAt ?? [];
  const pinModeOps = ops.filter((u) => u.operation.startsWith("pinMode:"));
  const distinctModes = new Set(pinModeOps.map((u) => u.operation));
 
  if (distinctModes.size > 1) {
    const modeNames = Array.from(distinctModes).map((op) => {
      const n = Number.parseInt(op.split(":")[1], 10);
      return pinModeToString(n);
    });
    return {
      conflict: true,
      conflictMessage: `Multiple modes: ${modeNames.join(", ")}`,
    };
  }
 
  const hasInput = ops.some(
    (u) => u.operation === "pinMode:0" || u.operation === "pinMode:2",
  );
  const hasWrite = ops.some(
    (u) => u.operation === "digitalWrite" || u.operation === "analogWrite",
  );
  if (hasInput && hasWrite) {
    const inputModeName = ops.some((u) => u.operation === "pinMode:2")
      ? "INPUT_PULLUP"
      : "INPUT";
    return {
      conflict: true,
      conflictMessage: `Write on ${inputModeName} pin`,
    };
  }
 
  const hasOutput = ops.some((u) => u.operation === "pinMode:1");
  const hasRead = ops.some(
    (u) => u.operation === "digitalRead" || u.operation === "analogRead",
  );
  if (hasOutput && hasRead) {
    return { conflict: true, conflictMessage: "Read on OUTPUT pin" };
  }
 
  return { conflict: false };
}