Hoppa till innehållet

Den här sidan zoomar: att scrolla tar dig närmare, från ett namn in i en mening, in i ett projekt, in i dess kod. Använd piltangenterna för att stega mellan djupen, eller läs den platta versionen på /sv/all.

Ditt system ber om mindre rörelse, så den här sidan ligger platt.

Vättern Visingsö E4 40 26 47 32 Huskvarna Gränna Habo Mullsjö Bankeryd Tranås Aneby Tidaholm Taberg Tenhult Bottnaryd Skärstad Jönköping 0 10 20 km N 57°47′ N · 14°10′ E kartdata © OpenStreetMap

Ismail Safwat

Jag bygger mjukvara i hela stacken: webbapplikationer, tjänsterna bakom dem, firmwaren inuti industriella maskiner och en gång, för nöjes skull, en spelmotor.

Jag bygger mjukvara i hela stacken: webbapplikationer, tjänsterna bakom dem, firmwaren inuti industriella maskiner och en gång, för nöjes skull, en spelmotor.

den här sidan · 2026 · eget

ismailsafwat.com – den här webbplatsen

Den här sidan: en enda sida som zoomar från ett namn ner till en bit kod, byggd på Astro och serverad från Cloudflares kant.

Astro · TypeScript · CSS · Cloudflare Pages · Cloudflare Workers

gränssnitt · applikation · tjänster · data

Ismail Safwat 1× Ismail Safwat 4× Wexiödisk, 2023 → 20× Wexiödisk · 2023 → Mjukvaruingenjör en bit av koden ↓ 80× s = mål.w / nästa.w k = 1 / s FÖRSTORING MOT SCROLL · log 1× 10× 100× 1000× 0 1 2 3 4 · djup ≈ 330× out: translate((cx−tx)·p, (cy−ty)·p) scale(k^p) in: translate((tx−cx)·(1−p), …) scale(s^(1−p)) omgivningen tonar 0.03–0.28 · målet 0.28–0.50 · nästa 0.44–0.66 prefers-reduced-motion → samma dokument, platt 1 2 3 4 5
gränssnitt
applikation
tjänster
data
firmware
en bit av koden ↓

Wexiödisk · 2023 → · arbete

Mjukvaruingenjör på Wexiödisk

Jag skriver styrmjukvaran och pekgränssnittet i Qt/QML inuti Wexiödisks industriella diskmaskiner, och telemetrin som rapporterar varje maskin tillbaka till molnet. Firmware som måste vara rätt i ett kök mitt på dagen, och skärmar som fungerar med blöta händer.

C++ · QML · Linux · Rhapsody · IoT · Embedded Systems

gränssnitt · applikation · tjänster · data · firmware

GND POUR U1 ARM · 480 MHz LINUX · C++ Y1 · 25 MHz J1 · SENSORS T1 temp L1 level S1 door P1 press. K1 HEATER 16 A · 230 V K2 PUMP 8 A K3 DOOR LOCK M1 Wi‑Fi · BLE U2 PMIC F1 · 2 A D1 D2 D3 TP1–3 J3 · FPC 40p J2 RS‑485 ECO 65° 04:12 PROGRAM 2 · WASH WD‑TOUCH · 7" · QML MQTT · TLS 1.3 temp 64.8 · cycle 2 temp 64.5 · cycle 2 door closed · 04:12 120.00 84 A 1 2 3 4 5 6 7 8
gränssnitt
applikation
tjänster
data
firmware
en bit av koden ↓

Blixtvakt · 2022 · arbete

Blixtvakt — blixtvarningar i telefonen

På Zenon AB byggde jag om Blixtvakt, en Ionic-app som bevakar SMHI:s blixtdata och varnar när nedslag kommer nära, på en karta ritad från OpenStreetMap, med varningsinställningar man ställer in själv.

Ionic · TypeScript · OpenStreetMap · SMHI API · Push notifications

gränssnitt · applikation · tjänster

14:02 Vättern 25 km 3 nedslag inom 25 km RADIE 25 km TYST 23–06 · LJUD PÅ IONIC · TYPESCRIPT SMHI blixtnedslag · API var 5:e minut lat · lon · kA · t OpenStreetMap kartrutor · z/x/y cachade på enheten appen hämta → filtrera → varna poll dist push haversine mot min position tiles nedslag → appen → telefonen VARNINGAR I DAG 11:40 13:15 13:52 14:02 tyst 23–06: inget ljud, bara banner 10 km 1 2 3 4 5
gränssnitt
applikation
tjänster
data
firmware
en bit av koden ↓

ett distribuerat affärssystem · 2022 · universitet

Distribuerat affärssystem

Ett universitetsprojekt där jag byggde ett system så som stora organisationer gör: flera tjänster som var och en äger sin egen data och pratar över REST. Det mesta jag vet om gränserna mellan tjänster lärde jag mig genom att få dem fel här först.

SOA · Microservices · Service Architecture · Enterprise Patterns

applikation · tjänster · data

TJÄNSTEZON · PRIVAT NÄT PUBLIK KANT client browser · JSON gateway auth · JWT rate limit route · retry REST · HTTP/1.1 account :8081 ctrl svc repo accounts person :8082 ctrl svc repo persons bank :8083 ctrl svc repo banks ETT LAGER PER TJÄNST GET /persons/{id} EN BEGÄRAN, TIDSLINJE · req 7f3a client gateway account person t=0 · POST /transfer +4 ms · auth ok +11 ms · owner? +23 ms · 200 +31 ms · 201 created +33 ms timeout 800 ms · 2 försök 1 2 3 4 5 6
gränssnitt
applikation
tjänster
data
firmware
en bit av koden ↓

Kundvagn · 2021 · universitet

Kundvagn – Android-app för kundvagn

Ett universitetsprojekt: en kundvagnsapp för Android i Kotlin. Det jag brydde mig om var synken: två telefoner, en kundvagn, och Firestore som håller dem ärliga i realtid.

Kotlin · Android SDK · Firebase · Firestore · Mobile Development

gränssnitt · applikation · data

09:41 09:41 kundvagn uid 3f…A mjölk×2 · 34 kr bröd×1 · 42 kr kaffe×1 · 69 kr betala · 145 kr TELEFON A · LÄGGER TILL kundvagn uid 3f…B mjölk×2 · 34 kr bröd×1 · 42 kr kaffe×1 · 69 kr betala · 145 kr TELEFON B · LYSSNAR Firestore europe‑west1 carts/ {uid}/ items/ k7f2 { name:"kaffe", qty:1 } a91c { name:"mjölk", qty:2 } c04e { name:"bröd", qty:1 } regler: auth.uid == uid index: items.addedAt ↑ offline: skrivningar köas set() onSnapshot onSnapshot set() INGEN ANSLUTNING set() väntar lokalt → spelas upp vid återanslutning ~200 ms skrivning till synlig ändring, Jönköping → europe‑west1 → Jönköping 1 2 3 4 5
gränssnitt
applikation
tjänster
data
firmware
en bit av koden ↓

Shape Shooters · 2020 · universitet

Shape Shooters – spelmotor i C++

Ett universitetsprojekt: ett arkadskjutspel för flera spelare och den lilla motorn under det, skrivet från grunden i C++ med SFML. Min första riktiga lektion i spelloopar, timing och varför kollisionsdetektering aldrig blir riktigt klar.

C++ · SFML · OOP · UML · Game Development

gränssnitt · applikation

SCORE 0420 LIVES 3 · WAVE 4 rutnät 80 px · 18 celler AABB 56² Ship · p(234,248) v(0,42) v(‑26,36) v(‑40,0) hit · +20 KLASSER Entity pos · vel · box Ship Enemy Bullet entiteter 14 kollisionstest 31 / 91 spar 66 % med rutnätet frame time · 60 Hz EN BILDRUTA · 16,7 ms input 0.4 · update 2.1 collide 1.3 render 6.8 idle 6.1 · vsync dt = clock.restart() · position += hastighet · dt 1 2 3 4 5
gränssnitt
applikation
tjänster
data
firmware
en bit av koden ↓
// Between depth i and i+1, p runs from 0 to 1.
// The outgoing layer grows around its target;
// the incoming layer shrinks onto that same target.
const s = target.width / incoming.width; // < 1
const k = 1 / s;

out.style.transform =
  `translate(${(cx - tx) * p}px, ${(cy - ty) * p}px) scale(${k ** p})`;

inc.style.transform =
  `translate(${(tx - cx) * (1 - p)}px, ${(ty - cy) * (1 - p)}px) scale(${s ** (1 - p)})`;
enum class State { Idle, Fill, Heat, Wash, Drain, Fault };

State next(State s, const Sensors& in) {
  switch (s) {
    case State::Idle:  return in.doorClosed && in.start ? State::Fill  : s;
    case State::Fill:  return in.level >= target.level  ? State::Heat  : s;
    case State::Heat:  return in.temp  >= target.temp   ? State::Wash  : s;
    case State::Wash:  return timer.expired()           ? State::Drain : s;
    case State::Drain: return in.level <= 0             ? State::Idle  : s;
    case State::Fault: return s;
  }
  return State::Fault;
}
const EARTH_KM = 6371;
function distanceKm(a: LatLng, b: LatLng) {
  const dLat = rad(b.lat - a.lat), dLon = rad(b.lon - a.lon);
  const h = Math.sin(dLat / 2) ** 2 + Math.cos(rad(a.lat)) * Math.cos(rad(b.lat)) * Math.sin(dLon / 2) ** 2;
  return 2 * EARTH_KM * Math.asin(Math.sqrt(h));
}

for (const strike of await smhi.latestStrikes()) {
  if (distanceKm(strike, here) <= settings.radiusKm && !settings.quietHoursNow()) {
    notify(`Blixt ${Math.round(distanceKm(strike, here))} km bort`);
  }
}
@GET
@Path("/accounts/{id}/balance")
public Response balance(@PathParam("id") String id) {
    Account account = accounts.find(id)
        .orElseThrow(NotFoundException::new);
    Person owner = personService.get(account.ownerId()); // another service, over REST
    return Response.ok(new BalanceDto(owner.name(), account.balance())).build();
}
cartRef.addSnapshotListener { snapshot, error ->
    if (error != null) return@addSnapshotListener
    val items = snapshot?.documents?.mapNotNull { it.toObject<CartItem>() }.orEmpty()
    _cart.value = items.sortedBy { it.addedAt }
}
while (window.isOpen()) {
    float dt = clock.restart().asSeconds();
    handleInput(window);
    for (auto& entity : entities) entity->update(dt, world);
    resolveCollisions(entities);
    window.clear();
    for (auto& entity : entities) entity->draw(window);
    window.display();
}
London Oslo Stockholm Helsingfors Köpenhamn Warszawa Amsterdam Frankfurt du ~ 12 ms sidan ligger här, och på varje annan punkt INGEN EGEN SERVER · EN BYGGNING, KOPIERAD UT TILL KANTEN
$ ./start-all gateway :8080 account :8081 person :8082 account-db :5432 person-db :5433 fem processer · en maskin · localhost koden kör här alla fem EN LAPTOP · LINKÖPINGS UNIVERSITET · 2022
kundvagn mjölk ×2 bröd ×1 kaffe ×1 145 kr kundvagn mjölk ×2 bröd ×1 kaffe ×1 145 kr samma kundvagn koden kör här två gånger A · min B · lånad ETT BORD I LABBET · LINKÖPINGS UNIVERSITET · 2021