dinsdag 28 december 2021

Load XML in Angular Typescript

It is possible to load XML in Angular:

let queryParams = new HttpParams();
queryParams = queryParams.append("version", "2.0.0");
queryParams = queryParams.append("service", "wfs");
queryParams = queryParams.append("request", "GetCapabilities");

this.http.get("https://geoserver.domain.com/test", { params: queryParams, responseType: 'text' }).pipe(
    map(res => {
        try {
            let parser = new DOMParser();
            let doc = parser.parseFromString(res, "text/xml");
            return doc;
        } catch (error) { console.error("error", error); }
    })
).subscribe({
    next: data => {
        if (data) {
            let nsResolver = data.createNSResolver(data.ownerDocument === null ? data.documentElement : (<any>data.ownerDocument).documentElement);
            let featureListNode = data.evaluate('/wfs:WFS_Capabilities/wfs:FeatureTypeList', data, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
            // /wfs:FeatureType/wfs:Name/text()
            if (featureListNode.singleNodeValue.hasChildNodes()) {
                const children = featureListNode.singleNodeValue.childNodes;
            }
        }
    },
    error: error => { console.error("error", error); }
});

Toevoegen: import { map } from 'rxjs/operators';

donderdag 18 november 2021

Branchless programming is sneller

Met "Branchless programming" probeer je branches te voorkomen. Dat doe je omdat de CPU de opkomende instructies in de pijplijn heeft en je niet wilt dat die verwijderd worden.

Branchless programming betekent niet dat je geen condities mag gebruiken.

In x64 bestaat de CMOVcc instructie. Die instructie is gemaakt voor branchless programming. De MOV wordt alleen gedaan als aan een conditie voldaan is. Daarmee hoef je dus geen branch (jump) te doen. Het code path blijft hetzelfde.

Zie deze youtube video.

maandag 15 november 2021

C# Newtonsoft JSON gebruiken i.p.v. default JSON serializer

nuget package Microsoft.AspNetCore.Mvc.NewtonsoftJson toevoegen. Ppak degene die bij je .NETCore versie past.

In de code, bijvoorbeeld in je startup moet je .AddNewtonsoftJson() toevoegen aan de elementen die je gebruikt:

Bijvoorbeeld:

services.AddMvc();
services.AddControllers();
services.AddControllersWithViews();
services.AddRazorPages();

wordt:

services.AddMvc().AddNewtonsoftJson();
services.AddControllers().AddNewtonsoftJson();
services.AddControllersWithViews().AddNewtonsoftJson();
services.AddRazorPages().AddNewtonsoftJson();


zondag 14 november 2021

Visual Studio CPP program crashes, now what?

So, my C/C++ program crashed it a random spot when running in release mode. What could be the problem?

Application Verifier 10.0 (x64) to the rescue!

It's in your Windows SDK, which normally is already installed with Visual Studio.

vrijdag 29 oktober 2021

Leaflet merged custom markers

It is possible to have custom markers in Leaflet. Those markers will be dynamically created by your Angular/Typescript or javascript code.

The marker is created using a canvas object. All the images are drawn in the canvas. That canvas is turned into base64. Why base64? Because it is good to cache the created markers, so you can reuse them. A nice way to cache a marker is turning it into a normal string using base64. That string can be used when creating your Leaflet L.Icon() object. Just put it in the iconUrl attribute. Shadows on your markers also look professional. You can easily configure them when creating a marker with L.icon().

let sources = [];  // will contain all properties about the images

// load a .png from the webserver. Position and width and height is supplied.
sources.push({ src: '/assets/img/icon.png', x: 0, y: 0, dx: 20, dy: 20 });

// or load a svg
let svgStr = '<svg> .... </svg>';
let svgDataStr = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponentsvgStr 
sources.push({ src: svgDataStr, x: 0, y: 0, dx: 20, dy: 20 });

const images = sources.map(source => new Promise((resolve, reject) => {
    const img = new Image();
    img.onerror = () => {
        reject(new Error('Couldn\'t load image'));
    };
    img.onload = () => {
        source["image"] = img;
        resolve(source);
    };
    img.src = source.src;
}));

let mergedData;
await Promise.all(images).then(images => {
    let canvas = document.createElement('canvas');
    canvas.width = 40;
    canvas.height = 40;
    const context = canvas.getContext('2d');

    images.forEach(image => {
        context.globalAlpha = 0.65; // a bit transparent
        context.drawImage(image["image"], (image["x"] || 0) + offsetX, (image["y"] || 0) + offsetY, image["dx"], image["dy"]);
    });
    mergedData = canvas.toDataURL('image/png', 0.92);
});

const icon = L.icon({
    iconUrl: mergedData,
    shadowUrl: ...
});
leafletMarker.setIcon(icon);

donderdag 9 september 2021

NPM registry toevoegen

npm c set @subdir:registry=https://npmstore.ipadres.local/repository/npm-publish/
npm c set strict-ssl false

npm i

ng serve -c conf --open

npm config list

maandag 7 juni 2021

Het fragmentatie pattern

Bij het fragmentatie pattern heb je geen programma-flow meer. Voor iedere miniscule state wordt een class aangemaakt, waardoor je een enorme kerstboom krijgt waar niemand meer een flow in kan ontdekken. Het liefst wordt daarbij Reactive ingezet, zodat je flow verdwenen is tijdens het debuggen.

De classes en methods krijgen geen namen die herkenbaar zijn binnen het businessdomein dat je probeert te automatiseren, want dat is veel te hard-coded. Alle classes dienen abstracte namen te krijgen.

De classes moeten klein zijn en de methods hebben maximaal 10 coderegels, die allemaal een miniscule verantwoordelijkheid hebben. Daardoor is de herbruikbaarheid maximaal en dat doe je dan ook overal in de code, zelfs bij code die niets met het ontstaan van de class te maken heeft. Een wijziging in een class gaat daarmee maximaal andere ongerelateerde code raken.

Tevens heeft iedere class ook een bijbehorende interface, want een concrete implementatie is yukkie en moet altijd een protocol worden. Je weet immers nooit of je nog een alternatieve implementatie zou willen maken.

Typisch heb je een hoop lege classes in je solution en dat vind je niet vies, want je weet maar nooit of je in de toekomst deze lege classes nog gaat vullen.

Overal moeten "principles" en design-patterns ingezet. Het liefst iedere maand nieuwe principles en patterns. Tegenwoordig zijn het Liskov Substitution Principle, Interface Segregation Principle, Flyweight pattern, Memento Pattern en Interpreter Pattern in de mode. Maar regelmatig moet je een nieuw pattern in de code toevoegen. Uitleg is verder niet nodig, want design-patterns zijn algemeen bekend.

</sarcasme>

zaterdag 29 mei 2021

Float datatype eenvoudig uitgelegd

Fabien Sanglard legt het veelgebruike type float goed uit. Float is een IEEE 754 standaard.

Kortgezegd heb je 1 sign bit, 8 exponent bits en 23 mantissa bits.

Het exponent gedeelte bepaalt in welk gebied het getal zit. Is de exponent 127, dan ligt het getal ergens tussen 1 en 2. Is de exponent 128 dan ligt het getal ergens tussen 2 en 4, etc.. Is de exponent kleiner dan 127, dan kom je in gebieden onder de 1 uit. Bij een exponent van 125 ligt het getal bijvoorbeeld ergens tussen 0.25 en 0.5.

Het mantissa gedeelte bepaalt waar precies in het gebied het getal is. De gradering is 2 tot de macht 23. Dat is bij kleine gebieden heel klein, maar dat loopt snel terug.

Bij exponent 150 (127+23) is iedere mantissa verhoging precies een verhoging van 1 in het float getal. Dus tot en met 2 tot de macht 24, dat is 16777216 kan ieder heel getal exact weergegeven worden. Daarna ga je sprongen krijgen. 16777217 kan bijvoorbeeld niet gemaakt worden, wel 16777218 bij een mantissa van 1 (mantissa van 0 is in dat geval 16777216).

Uitzonderingen:
Om het getal 0 weer te geven moet je sign 0 hebben, exponent 0 en mantissa 0.
Het getal infinity sign 0, exponent 255 en mantissa 0.
Het getal NaN (not a number) is sign 0, exponent 255 en mantissa ((2^23)-1) oftewel 8388607. Een mantissa van 1 is een Signaling NaN. Het verschil tussen een NaN en een SNaN is dat een SNaN een error veroorzaakt in de FPU module, al heb ik nog niet een SNaN error weten te genereren vanuit software.

Voor de C ontwikkelaars:
    union {
        float theFloat;
        struct {
            unsigned int mantissa : 23;
            unsigned int exponent : 8;
            unsigned int sign : 1;
        } parts;
        Uint32 theULong;
    } f;

    f.theFloat = 0.0f;
    int mantissa = f.parts.mantissa;
    int exponent = f.parts.exponent;
    int sign = f.parts.sign;

Als een float NaN is, dan is f.theFloat != f.theFloat. De float value is dan dus niet gelijk aan zichzelf.

Kleine oefening. Om een mantissa te krijgen die 1 groter is dan het begingetal, doe:

    int exponentVerhoging = 23;
    int beginGetal = 1 << exponentVerhoging;
    f.parts.exponent = 127 + exponentVerhoging;
    long mantissaGetalBeginPlus1 = (1 << 23) / (1 << exponentVerhoging);
    f.parts.mantissa = mantissaGetalBeginPlus1;

Toevoeging 2021-05-30: de "double" variant is soortgelijk. 1 sign bit, 11 exponent bits, 52 mantissa bits.
Daarmee kun je ieder getal tot en met 2 tot de macht 53 exact weergeven, dat is 9007199254740992. Het eerste getal dat niet weergegeven kan worden is dus 9007199254740993.
Het getal 1 is sign 0, exponent 1023, mantissa 0.
Het getal infinity is sign 0, Exponent 2047 en mantissa 0. Het getal 0 weer alles 0.

Opvallend: Wist je dat onder x64 in C/C++ het datatype long 4 bytes groot is? Net als een int, die ook 4 bytes groot is. Pas een "long long" is 8 bytes groot, maar dan gebruik ik liever de alias Sint64 om de duidelijkheid erin te houden. Eigenlijk is het duidelijker om overal Sint32 en Sint64 of Uint32/Uint64 te gebruiken als je met bits manipulatie bezig bent.

dinsdag 25 mei 2021

Waarom DOOM niet extreem soepel loopt op nieuwe CPU's

Een tijdje geleden had ik DOOM BFG edition aangeschaft en het viel mij op dat DOOM niet extreem soepel loopt, terwijl moderne CPU's toch veel sneller zijn dan die van 1993.

De reden hiervoor is de opbouw van het beeld bij DOOM. De muren worden namelijk verticaal getekend door de oorspronkelijke engine. Dus iedere keer een verticale rij van 1 pixel breed. Dat is echt killing voor je CPU cache. Daardoor heb je weinig aan de snelheid van je CPU, want de CPU is alleen maar aan het wachten op je memorybus.

Eerder had ik al geschreven over cachelines. Bij het tekenen van muren in SunRacer heb ik initieel ook geprobeerd om muren verticaal te tekenen, dus 1 pixel breed. Dat was zo langzaam dat het soms niet eens lukte om het hele scherm (1920x1080) te tekenen binnen 1 frame.

De CPU kan 64 bytes tegelijkertijd naar het geheugen wegschrijven, maar als de software vervolgens maar 4 bytes (1 pixel) verandert, dan gooi je een hoop performance weg.

Tegenwoordig houd ik in SunRacer meer rekening met de horizontale cachelines en bereken zoveel mogelijk pixels horizontaal. De CPU heeft dan wel meer werk, omdat er meer programmacode wordt doorlopen per pixel. Maar de CPU stond toch niets te doen. Dus is het uiteindelijke resultaat toch 2 keer sneller.

Waarom kost vermenigvuldigen minder clockcycles dan delen?

De oorspronkelijk Pentium CPU kon in 10 clockcycles een multiply doen, maar had voor een division 41 clockcycles nodig. Waarom is dat?

Als je kijkt naar onze schoolmethoden voor het vermenigvuldigen en delen, dan kun je het antwoord raden.

Vermenigvuldigen doen we namelijk door per cijfer een vermenigvuldiging te doen en de berekende getallen uiteindelijk op te tellen. De vermenigvuldigingsstappen zijn parallel uit te voeren. De CPU doet die stappen dus ook parallel.

Het delen doet de CPU op een soortgelijke methode als onze staartdeling. Die stappen kun je echter niet parallel uitvoeren, want de volgende stap is afhankelijk van de vorige. De CPU kan het probleem dus niet makkelijk parallel oplossen. Daarom duurt het langer.

Je ziet het terug in de logische diagrammen voor multiply en division. Op CPU's die geen float division kunnen doen kun je je wenden tot benaderingen.

woensdag 14 april 2021

Postgresql paste

create or replace function create_partitions()
returns void
language plpgsql
as $$
declare
  c_date_format constant varchar(8) := 'yyyymmdd';
  meuk varchar(255);
begin
  for r in (select tablename from pg_tables where tablename like 'history_%' and substring (tablename from '........$' < to_char(now() + '-7 day', c_date_format))
  loop
     meuk := r.tablename;
     raise notice '%', meuk;
     execute format ('drop table %1$s', r.tablename);
  end loop;
end;
$$;


do $$
declare
distance float;
begin
distance := (select ST_Distance(ST_Transform(old."location"::geometry, 3857), ST_Transform(new."location"::geometry, 3857)));
if (distance > 4.0) then
   perform create_partitions();
end if;
end;
$$


select create_partitions()

select cron.schedule('10 2 * * *', $$select create_partitions()$$);
-- iedere 5 minuten is: '*/5 * * * *'
select * from cron.job
select cron.unschedule(jobid) from cron.job where jobid = {jobid getal}
select * from history where date > TIMESTAMP 'yesterday'

maandag 22 maart 2021

ssl binding on port on windows

 On Windows, it is possible to bind a SSL certificate to a port.

netsh http show sslcert

netsh http add sslcert ipport=0.0.0.0:4000 certhash=casda... appid={123123..123123}

netsh http delete sslcert ipport=0.0.0.0:4000

The appid can be randomly choosen, because it is only a id to identify the entry.
The certhash is the thumbprint found in the certificate.

maandag 22 februari 2021

pg_dump remote server tables

Je moet eerst even in pgpass.conf in %APPDATA% aanmaken.

Dus:
cd %APPDATA%
notepad pgpass.conf

Inhoud pgpass.conf

dnsentryname.local:5432:*:user:password
localhost:5432:*:postgres:password

Vervolgens:

pg_dump -h ipadres --column-inserts --data-only -t tablename -t othertablename --username=username --file=output.dmp --dbname=db -w

maandag 1 februari 2021

SQL: Update using a select

begin transaction;

update targettable
set "location" = CAST(ST_SetSRID( ST_Point(subquery.longitude_::float, subquery.latitude_::float), 4326) AS geography)
from (
select id, ajsonb -> 'location' as location_, ajsonb->'location'->'latitude' as latitude_, ajsonb->'location'->'longitude' as longitude_
from targettable) as subquery
where targettable.id = subquery.id
and subquery.location_ is not null

commit;
--rollback;

woensdag 20 januari 2021

Door welk process is poort 80 in gebruik

 netstat -aon | findstr :80

 If you would like to free the port, go to Task Manager, sort by pid and close those processes.

-a displays all connections and listening ports.
-o displays the owning process ID associated with each connection.
-n displays addresses and port numbers in numerical form.

Nginx restart

nginx staat meestal in /etc/nginx

Herstarten:
sudo su
systemctl reload nginx

of

systemctl restart nginx