Løse noter om programmering og andet “Work In Progress”.
“Hvis du nyder at veksle mellem at føle dig som den klogeste i verden og historiens største fjols, begge dele i løbet af den samme dag, så er programmering sikkert lige noget for dig.”
– Anonym programmør
Subsections of Getsrevel
Teknologi
Eksempler med teknologier vi arbejder med i programmering.
Subsections of Tech
Html
HTML er et opmærkningssprog, der bruges til at kommunikere strukturen af indholdet i et dokument til en browser.
Her er et eksempel på en html struktur.
<!DOCTYPE html><htmllang="en">
<head>
<metacharset="UTF-8" />
<metaname="viewport"content="width=device-width, initial-scale=1.0" />
<title>Titlen på dokumentet</title>
</head>
<body>
Indhold på siden
</body>
</html>
HTML elementer
Når du skriver dit indhold benyttes en række forskellige tags, til at angive hvordan de forskellige dele (HTML elementer) af dokumentet skal fortolkes.
Typisk ender et dokument med at bestå af en række HTML elemementer, hver med et eller flere indlejrede dokumenter indeni.
Ikke afsluttede tags
Det er ikke alle HTML elementer, hvor det semantisk giver mening at de har indhold. Derfor kan de også bestå af et tag der slutter sig selv.
Et eksempel er hr (Horizontal rule)
<hr />
Et andet eksempel er når der indsættes et billede. Her angives en URL til hvor billedets kilde kan findes som en attribut.
<imgsrc="path/to/image/file.jpg"title="En beskrivende title"alt="Beskrivelse af billedet i fald det ikke kan vises" />
Alle de mange tags og hvordan de bruges kan du læse mere om i denne HTML Tutorial.
Demo dokument
Her er et lidt mere fyldigt eksempel.
<!DOCTYPE html><htmllang="en">
<head>
<metacharset="UTF-8" />
<metaname="viewport"content="width=device-width, initial-scale=1.0" />
<title>Titlen på dokumentet</title>
</head>
<body>
<h1>Eksempel på HTML dokument</h1>
<p>Dette er et afsnit med noget tekst. Bemærk at jeg har sat et billede ind herunder</p>
<imgsrc="IMG_9747.jpg"alt="kameleon i skovbunden"title="Hovedet skiftede fra rødt til grønt mens jeg fandt kameraet frem" />
<hr />
<p>Her følger en liste med et par HTML elementer.</p>
<ul>
<li>punkt 1</li>
<li>punkt 2</li>
<li>punkt 3</li>
</ul>
<p>Man kan nemt ændre det til en ordnet liste.</p>
<ol>
<li>punkt 1</li>
<li>punkt 2</li>
<li>punkt 3</li>
</ol>
</body>
</html>
Formålet med eksemplet er ikke at lave en webside, der er visuelt pæn, men blot at illustrere et udvalg af hvordan styles og selectors kan bruges.
Her er html strukturen i demo.html.
Bemærk hvordan det eksterne stylesheet er inkluderet, definition af style internt i dokument, samt brug af inline style.
<!DOCTYPE html><htmllang="en">
<head>
<metacharset="UTF-8">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<linkrel="stylesheet"type="text/css"href="external-style.css" >
<title>CSS på 3 måder</title>
<style>
h3 {
font-style: italic;
}
</style>
</head>
<body>
<header>
<h2>Ekstern Definition</h2>
<p>Udseendet af denne sektion er defineret i en ekstern css fil.</p>
<p>Selector er lavet vha. af elementets navn.</p>
</header>
<main>
<h1>Tre måder at bruge CSS i HTML</h1>
<ul>
<li>Ekstern CSS Fil.</li>
<li>Intern CSS i dokument.</li>
<li>Inline CSS definition i style attribut.</li>
</ul>
<h3>Intern definition</h3>
<p>Som eksempel er der lavet en <strong>intern definition</strong>, der ændrer skriften til kursiv for <code>h3</code> elementer.</p>
<h2>Selectors</h2>
<p>Der er flere måde at udvælge elementer vha. selectors. De mest almindelige er:</p>
<ul>
<li>Direkte ved hjælp af navn på elementet.</li>
<li>Ved brug af en klasse.</li>
<li>Ved brug af et ID.</li>
</ul>
<div>
<h3>Element</h3>
<p>Man kan bruge html elementer direkte som selector, så vil alle elementer der matches bruge samme stil.</p>
<p>Dette er lavet som eksempel i dette dokument, med <code>div</code> elementer Der bliver vist som indrammede kasser.</p>
</div>
<divclass="my-custom-class">
<h3>Klasse</h3>
<p>Man kan bruge selectors til at vælge bestemte html elementer vha. deres navn. Dette er lavet som eksempel i dette dokument, med <code>div</code> elementer Der bliver vist som indrammede kasser.</p>
<p>Man kan bruge html elementer direkte som selector, så vil alle elementer der matches bruge samme stil.</p>
</div>
<divid="my-custom-id">
<h3>Selector med ID</h3>
<p>Man kan vælge at tildele et ID som en attribut til et element, og bruge det til styling.</p>
<p>For denne blok er stilen lavet med et ID</p>
<p><em>Bemærk:</em> at et id skal være unikt indefor et dokument.</p>
</div>
<divclass="my-custom-class">
<h3>Klasser fortsat</h3>
<p>Man kan bruge Samme klasse flere steder i sit dokument.</p>
</div>
</main>
<footer>
<h2>Demonstration af css</h2>
<p>Her er et eksempel på en <spanstyle="font-weight: bold; color: orange;">inline style</span></p>
</footer>
</body>
</html>
Her er css filen strukturen i external-style.css
/* Man kan indsætte kommentarer i CSS på denne måde *//*
Kommentarer
kan sprede sig
over flere
linjer
*/header {
display: inline-flexbox;
justify-content: flex-end;
margin: 1em1em;
padding: 1em;
background-color: yellow;
border-radius: .5em;
-webkit-box-shadow: 8px13px16px0px rgba(0, 0, 0, 0.85);
-moz-box-shadow: 8px13px16px0px rgba(0, 0, 0, 0.85);
box-shadow: 8px13px16px0px rgba(0, 0, 0, 0.85);
}
body {
margin: 0;
}
footer {
background-color: gray;
padding: .2em3em;
margin: 0;
}
h3 {
color: brown;
font-style: italic;
}
main {
margin: 02em;
}
div {
background-color: lightgrey;
margin-bottom: 1em;
padding: .1em1em;
border: 2pxsolidgray;
}
.my-custom-class {
color: rgb(0, 92, 0);
background-color: rgb(136, 207, 136);
border-color: green;
}
#my-custom-id {
background-color: lightpink;
border-color: darkred;
margin-left: 2em;
margin-right: 2em;
border-radius: .5em;
border-width: 5px;
}
Dette eksempel viser hvordan man kan bruge css grid til at lave et responsive layout på en webside.
Det består af et enkelt html dokument og en tilhørende css fil.
Her er den html struktur der er brugt.
<!DOCTYPE html><htmllang="en">
<head>
<metacharset="UTF-8">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<title>CSS grid layout</title>
<linkrel="stylesheet"type="text/css"href="style.css" >
</head>
<body>
<divclass="grid-container">
<divclass="item1"><h2>
Header
</h2>
<p>Prøv at ændre i bredden på vinduet og læg mærke til hvad der sker med layoutet.</p>
</div>
<divclass="item2">
<h2>Menu</h2>
<ul>
<li><ahref="#">Menu item 1</a></li>
<li><ahref="#">Menu item 2</a></li>
<li><ahref="#">Menu item 3</a></li>
<li><ahref="#">Menu item 4</a></li>
<li><ahref="#">Menu item 5</a></li>
</ul>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit.</p>
</div>
<divclass="item3">
<h2>Main</h2>
<divclass="breaking">
<h2>Under udarbejdelse</h2>
</div>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aperiam tenetur, quidem reiciendis itaque beatae dolores consequuntur eos fugiat iste consequatur rem molestias, exercitationem veniam nisi iure minima fuga illum ea.</p>
<p>Quod voluptates accusantium rerum a cum, quidem modi velit voluptatum magnam corrupti repudiandae minus, eveniet ipsum sunt soluta labore aliquam possimus. Natus tempora itaque eos aliquid earum quae asperiores blanditiis!</p>
<p>Itaque velit molestias provident amet magni voluptas, voluptatibus maxime esse. Quaerat id molestias odio dolore animi nemo, suscipit modi magnam quasi temporibus omnis sint natus ducimus dignissimos labore fugit voluptatem!</p>
<p>Similique vitae architecto sequi error soluta nobis iste quos voluptatem expedita repudiandae numquam fugit fugiat, debitis omnis incidunt ipsam possimus sint magni quas cumque aspernatur! Voluptatem saepe sed quis corporis.</p>
</div>
<divclass="item4">
<h2>Right</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia, repellat. Ducimus accusamus temporibus cum perspiciatis, incidunt illo saepe error ex ad reiciendis at magni, deleniti velit in voluptatibus iusto eos.</p>
<p>Voluptatum, dicta quis iste cumque magni harum quaerat cupiditate adipisci culpa libero quae accusantium praesentium excepturi quod eum obcaecati dolorum iusto aperiam est. Soluta, enim? Culpa autem temporibus rem eaque.</p>
<p>Possimus animi minima aliquam molestiae laboriosam sit aliquid tenetur ut consectetur deserunt architecto, perspiciatis facere earum magnam totam minus quisquam incidunt placeat optio quidem! Minima, repellendus. Natus dolorum optio ducimus.</p>
<p>Expedita aliquid, quam iusto voluptas totam possimus laboriosam earum fugiat ducimus, esse soluta rerum, non iure. Ex nemo animi molestias aliquid aperiam placeat ducimus qui numquam provident nostrum, magnam ipsa.</p>
</div>
<divclass="item5"><h2>Footer</h2></div>
</div>
</body>
</html>
Dette eksempel viser hvordan man kan måle tiden mellem to hændelser.
Pin 2: starter timer når forbindelsen til GND afbrydes.
Pin 3: stopper tidtagningen når forbindelsen til GND sluttes.
Den indbyggede LED er tændt, mens tidtagningen er i gang.
constint startPin =2;
constint gatePin =3;
constint ledPin =13;
int isStarted = false;
int hasTriggeredGate = false;
unsignedlong startTime =0;
unsignedlong gateTime =0;
voidsetup() {
pinMode(ledPin, OUTPUT);
pinMode(startPin, INPUT_PULLUP);
pinMode(gatePin, INPUT_PULLUP);
Serial.begin(9600);
Serial.println("Ready...");
}
voidloop() {
int startState =digitalRead(startPin);
// Turn on LED while measurement is running
int measRunning = isStarted &&!hasTriggeredGate;
digitalWrite(ledPin, measRunning);
// Handle start trigger
if (HIGH == startState &&!isStarted) {
startTime =millis();
isStarted = true;
Serial.print("Start time 1: ");
Serial.println(startTime);
}
// Handle gate trigger
int gateState =digitalRead(gatePin);
if (LOW == gateState &&!hasTriggeredGate) {
gateTime =millis();
hasTriggeredGate = true;
Serial.print("Gate time: ");
Serial.println(gateTime);
Serial.print("Time diff: ");
unsignedlong timeDiff = gateTime - startTime;
Serial.println(timeDiff);
}
}
Neopixel
Dette eksempel laver et “løbelys” med blå farver.
Hardware setup
Arduino og LED array hardware
LED modulerne er forbunde i en kæde, så data signalets output er forbundet til input på næste LED modul.
Alle modulerne forsynes med 5V fra arduino.
Der er monteret en kondensator for at udjævne spændingen.
Datasignalet tages fra pin 6 på Arduino.
Arduino & NeoPixel
For at kunne styre arrayet af NeoPixel LED’er kan du benytte softwarebiblioteket Adafruit Neopixel.
Det kan tilføjes til din Arduino IDE installation ved at bruge udvidelsesværkttøjet (Værktøjer –> Manage libraries…), som vist på dette skærmbillede.
Søg efter neopixel i Arduino Library manager
Der følger en del eksempelkode med i til Neopixel biblioteket til Arduino.
Denne guide beskriver hvordan man bruger eksempler i Arduino.
Prøv f.eks. det der hedder simple, i Adafruit Neopixel biblioteket.
Husk at ændre antallet af LED’er i koden, så det passer til hardwaren.
Bemærk også, at du skal bruge samme output pin i koden, som du har forbundet med ledninger.
Kode eksempel
Her er en sketch, der laver et “løbelys” med blå farver.
#include<Adafruit_NeoPixel.h>// Which pin on the Arduino is connected to the NeoPixels?
#define PIN 6 // On Trinket or Gemma, suggest changing this to 1
// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 10
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
// Time (in milliseconds) to pause between pixels
#define DELAYVAL 500
voidsetup() {
// INITIALIZE NeoPixel strip object (REQUIRED)
pixels.begin();
}
voidloop() {
pixels.clear(); // Set all pixel colors to 'off'
// The first NeoPixel in a strand is #0, second is 1, all the way up
// to the count of pixels minus one.
for(int i=0; i<NUMPIXELS; i++) { // For each pixel...
// pixels.Color() takes RGB values, from 0,0,0 up to 255,255,255
pixels.setPixelColor(i, pixels.Color(0, 20, 150));
// Send the updated pixel colors to the hardware.
pixels.show();
// Pause before next pass through loop
delay(DELAYVAL);
}
}
Dette eksempel viser hvordan man kan bruge JSON data formatet til at udveksle information med en arduino og sende kommandoer via seriel porten.
Ved at sende en passende kommando kan man ændre på hastighed og duty cycle for en blinkende LED.
For nemt at kunne arbejde med JSON i Arduino koden benyttes biblioteket Arduino JSON.
Derfor er det nødvendigt at installere dette på udviklingsmaskinen, inden denne sketch kan kompileres, det klares via. Arduino Library Manager, se hvordan du installerer det her.
#include<ArduinoJson.h>// Allocate the JSON document
//
// Inside the brackets, 200 is the capacity of the memory pool in bytes.
// Don't forget to change this value to match your JSON document.
// Use arduinojson.org/v6/assistant to compute the capacity.
// StaticJsonDocument<200> doc;
// StaticJsonDocument<N> allocates memory on the stack, it can be
// replaced by DynamicJsonDocument which allocates in the heap.
//
DynamicJsonDocument doc(200);
int periodMs =2000;
int dutyPct =50;
voidsetup()
{
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
// wait for serial port to connect. Needed for native USB
while (!Serial)
{
continue;
}
Serial.println("Ready for commands");
}
voidloop()
{
myLedControl();
parseCommands();
}
// Example of test input json
/*
{"ts": 1000, "duty": 10 }
*/voidparseCommands()
{
// reply only when you receive data:
if (Serial.available() >0)
{
// Deserialize the JSON document
DeserializationError error =deserializeJson(doc, Serial);
Serial.println("Received:");
serializeJson(doc, Serial);
Serial.println();
// Test if parsing succeeds.
if (error)
{
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
return;
}
if (doc["ts"])
{
unsignedint ts = doc["ts"];
Serial.print("got ts: ");
Serial.println(ts);
periodMs = ts;
}
if (doc["duty"])
{
int duty = doc["duty"];
Serial.print("got duty: ");
Serial.println(duty);
duty =constrain(duty, 0, 100);
Serial.print("constrained duty to [0, 100], using: ");
Serial.println(duty);
dutyPct = duty;
}
}
}
// toggle the builtin LED state
voidmyLedControl()
{
//map(value, fromLow, fromHigh, toLow, toHigh)
int highDelayMs =map(dutyPct, 0, 100, 0, periodMs);
int lowDelayMs = periodMs - highDelayMs;
digitalWrite(LED_BUILTIN, HIGH);
delay(highDelayMs);
digitalWrite(LED_BUILTIN, LOW);
delay(lowDelayMs);
}
NB! Serielporten er sat til at køre 115200 Baud.
Prøv at sende nogle kommandoer vha. serial monitor, og læg mærke til hvordan blinkrate og duty cycle ændres for den inbyggede LED på Arduino.
{"ts": 1000, "duty": 10 }
{"ts": 500, "duty": 80 }
Når eksemplet køres på en Arduino og de to ovenstående kommandoer sendes en ad gangen, kommer der dette output fra Arduino på seriel porten.
Dette eksempel illustrerer, hvordan man kan bruge function pointers til at køre forskellige opgaver med hvert sit interval, uden at de blokerer for hinanden i længere tid end det tager at eksekvere en enkelt opgave.
typedefstruct MyTask {
void (*handler)();
int intervalMs;
int taskId;
unsignedlong lastRunMs;
} MyTask;
// Forward declaration of the task runner functions
voidtask_main();
voidtask_A();
voidtask_B();
voidtask_C();
MyTask tasks[] = {
{.handler=task_main},
{task_A,300},
{task_B,500},
{task_C,3000}
};
constint arrSize =sizeof(tasks)/sizeof(MyTask);
voidsetup() {
// initialize serial communication at 115200 bits per second:
Serial.begin(115200);
Serial.print("Task count: ");
Serial.println(arrSize);
for(int i=0 ; i<arrSize; i++){
if(tasks[i].intervalMs <=0){
tasks[i].intervalMs = (i+1)*100;
}
String msg ="";
msg ="task id: ";
msg += i;
msg +=", interval ";
msg += tasks[i].intervalMs;
Serial.println(msg);
}
Serial.println("Setup DONE");
}
// example of non-blocking asyncronous wait loop using function pointers
voidloop() {
for(int i=0 ; i<arrSize; i++){
runInterval(&tasks[i]);
}
}
voidrunInterval(struct MyTask *t){
unsignedlong tickMs =millis();
unsignedlong diffMs = tickMs - t->lastRunMs;
// Handle first run
if(0== t->lastRunMs){
t->lastRunMs = tickMs;
}
bool shouldRun = (t->intervalMs < diffMs);
if(!shouldRun){
return;
}
// Store last run
t->lastRunMs = tickMs;
// Perform the process by calling the handler function
(t->handler)();
}
// Create some tasks to run in the example
voidtask_main(){
staticint runCount =0;
Serial.print(".");
if(0== runCount %30){
Serial.println();
Serial.print(runCount);
Serial.print(" : ");
}
runCount++;
}
voidtask_A(){
Serial.print("A");
}
voidtask_B(){
Serial.print("B");
}
voidtask_C(){
Serial.print("C");
}
Når eksemplet køres på en arduino generes dette output på seriel porten.
Dette eksempel illustrerer hvordan man kan generere json output på seriel porten, og er tænkt som en en stub der kan bruges til at arbejde med seriel input på en en anden computer, f.eks. vha. node.js.
Der vælges en tilfældig værdi i tagIds arrayet, som så serialiseres som json sammen med oplysning om index og delay tiden, og udskrives på serielporten med tilfældige tidsintervaller.
Det er også mulighed for at vælge 2 bestemte tags ved at trække input pin 2 eller 3 lav, ved fysisk at forbinde til GND.
NB! Serielporten er sat til at køre 115200 Baud.
For nemt at kunne arbejde med JSON i Arduino koden benyttes biblioteket Arduino JSON.
Derfor er det nødvendigt at installere dette på udviklingsmaskinen, inden denne sketch kan kompileres, det klares via. Arduino Library Manager, se hvordan du installerer det her.
Eksempel på output
Output på serielporten kommer til at se ca. sådan ud.
Bemærk at det er kopieret fra Serial Monitor i Arduino IDE, og timestamps er slået til, derfor er der et tidsstempel på alle linier i output.
Tidstempler, delay intervaller og rækkefølgen af tagId’er vil være forskellig for hver kørsel, da de bliver genereret tilfældigt.
For at kunne arbejde med C# kildekoden har vi brug for en god editor.
Envidere får er der brug for en kompiler, så vi kan oversætte kildeteksten til et program der kan eksekveres på maskinen.
Code editor
Der er flere muligheder at vælge mellem når man skal arbejde med C# kode.
For ikke at bruge en masse tid på at lære et nyt miljø at kende, vælger vi at bruge Visual Studio Code, da den kan bruges både på Windows og Mac, og det er den samme editor vi har brugt til at arbjede med javascript.
Efter du har installeret Visual Studio Code vil det være en fordel at installere en udvidelse, der gør det nemmere at arbejde med C# filer, kopilering, og debugging af disse.
For at installere udvidelsen vælges plugin i menuen, og søg efter C#, på listen skulle du gerne kunne finde dette C# plugin fra microsoft.
Compiler
C# adskiller sig fra javascript bl.a. ved, at det er nødvendigt at oversætte kildeteksten til et eksekverbart program, inden det kan afvikles på computeren.
Derfor har vi brug for en kompiler.
Efter installationen er færdig kan du åbne en terminal f.eks. Powershell og verificere din version af .NET Core med kommandoen dotnet --version.
På min maskine giver den dette output.
$ dotnet --version
5.0.401
Hvilket vil sige at jeg har version 5.0.401 installeret.
Opret projekt
Den nemmeste måde at starte et nyt .NET Core projekt er vha. terminalen.
Start med at åbne en terminal i den mappe du ønsker at arbejde i.
For at oprette et nyt projekt skal du åbne en terminal og køre kommandoen.
dotnet new console -o my-console-app
Dette laver en ny console app med navnet my-console-app, og opretter det en undermappe med samme navn.
Afvikling fra kommando linjen
For at sikre at det virker efter hensigten, starter vi med at oversætte og køre programmet fra kommando linjen.
Skift til det netop oprettede projekt med kommandoen:
cd my-console-app
Du står nu i mappen med projektet, og kan nu oversætte (compile) og køre (run) det med kommandoen dotnet run.
På min maskine giver det dette output.
my-console-app $ dotnet run
Hello World!
Hvis du kan få et lignede resultat er det lykkedes at kompilere og oversætte programmet.
Arbejd med C# i VS code
Nu er det vist tid til at rette lidt i koden, åben derfor mappen med projektet i VS code.
Åben filen Program.cs i projektet. Indholdet burde se således ud:
Prøv nu at rette den fremhævede linje, så den skriver Hej Programmering! i stedet for Hello World!.
Husk at gemme filen.
C# extension
For at gøre det nemmere at arbejde med C# i VS Code skal vi have installeret en program udvidelse.
Søg efter c# i extensions i VS code og tryk på install.
Screenshot af VS code hvor der er søgt efter C# udvidelsen.
Kompilering / Debug
Med den nye extension er det muligt at oversætte, køre og fejlsøge i C# programmer vha. knapper i VS Code.
For at køre programmet skal du vælge run -> debug i menuen, eller trykke på f5.
Måske kommer der en boks frem der beder dig vælge det udviklingsmiljø du vil bruge. Her skal du vælge .NET Core.
Dette genererer instillinger i mappen .vscode, som bruges af C# udvidelsen når koden skal oversættes og køres.
Når programmet kører får jeg på min maskine dette output:
-------------------------------------------------------------------
You may only use the Microsoft .NET Core Debugger (vsdbg) with
Visual Studio Code, Visual Studio or Visual Studio for Mac software
to help you develop and test your applications.
-------------------------------------------------------------------
Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.10\System.Private.CoreLib.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Loaded 'C:\code\c-sharp\2021\my-console-app\bin\Debug\net5.0\my-console-app.dll'. Symbols loaded.
Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.10\System.Runtime.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.10\System.Console.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.10\System.Threading.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.10\System.Text.Encoding.Extensions.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Hello World!
The program '[14664] my-console-app.dll' has exited with code 0(0x0).
Det meste er beskeder fra compiler og runtime miljøet, men der kommer også det forventede output fra koden jeg selv har skrevet, på den fremhævede linje.
Håndtering af stdin / stdout
Afhængig af typen af det program du laver, kan det være hensigtsmæssigt at ændre måden det eksekveres i fra VS Code på.
Der er 3 muligheder, som vælges ved at ændre i instillingerne i .vscode/launch.json.
Prøv at ændre i parameteren console til en af disse værdier, og vælg den der passer bedst til dit brugsscenarie.
"console": "internalConsole"
"console": "integratedTerminal"
"console": "externalTerminal"
Hvis jeg vælger indstillingen externalTerminal, vises output fra mit program fra tidligere på denne måde på min windows 10 maskine.
Screenshot af terminal output.
Gitignore
Når .NET Core projektet bygges, bliver der genereret en række objektfiler og binære biblioteker osv. af kompileren. Disse er nødvendige for at programmet kan afvikles, men hvis de andre der arbejder selv har en kompiler installeret, kan disse nemt gendannes.
Derfor kan det være nyttigt at undlade at tilføje genererede filer til versionsstyringssystemet.
Hvis man f.eks. benytter git kan man lave en liste af filer og mapper der skal ignoreres af git.
Dette kan klares ved at oprette en fil med navnet .gitignore, i roden af projektet.
# files and folders to be ignored by git# ignore build artifactsbin/
obj/
Med dette indhold bliver indholdet af mapperne bin og obj ignoreret af git.
Man kan nemt justere i hvilke filer og mapper der skal ignoreres ved at ændre i .gitignore filen.
Se også dokumentationen af gitignore.
Eksemplet viser hvordan man kan bede en bruger om input, og bruge det til at vurdere temperaturen.
NB! Temperaturen er i grader celcius.
using System;
namespace temperature_assessment
{
classProgram {
private InputHandler inputHandler;
private TemperatureAssessor temperatureAssessor;
public Program(InputHandler ih, TemperatureAssessor ta)
{
this.inputHandler = ih;
this.temperatureAssessor = ta;
}
publicvoid Run()
{
do {
double temperature = inputHandler.PromptForNumber("Hvad er temperaturen i grader celsius?");
string result = temperatureAssessor.EvaluateTemperature(temperature);
Console.WriteLine(result);
} while (inputHandler.Confirm("Vil du prøve igen?"));
}
staticvoid Main(string[] args)
{
Console.WriteLine("Temperatur vurdering");
InputHandler inputHandler = new InputHandler();
TemperatureAssessor temperatureAssessor = new TemperatureAssessor();
Program myProgram = new Program(inputHandler, temperatureAssessor);
myProgram.Run();
}
}
classTemperatureAssessor {
publicstring EvaluateTemperature(double temperature)
{
if (temperature <= 10) return"For koldt til at stå op - bliv i sengen";
if (temperature <= 15) return"For koldt til at arbejde - bliv hjemme";
if (temperature <= 20) return"OK temperatur til en tur i skoven";
if (temperature <= 22) return"Perfekt pausetemperatur";
return"For varmt til at arbejde - tag til stranden";
}
}
classInputHandler {
publicdouble PromptForNumber(string message)
{
doublevalue;
bool isSuccess;
do {
string line = Prompt($"{message} ");
isSuccess = Double.TryParse(line, outvalue);
if (!isSuccess)
{
Console.Error.WriteLine("Ugyldigt input: Du skal skrive et tal.");
}
} while (!isSuccess);
returnvalue;
}
publicbool Confirm(string question)
{
string confirm = "ja";
string reject = "nej";
string response;
bool isValidResponse;
do {
response = Prompt($"{question} [{confirm}/{reject}] ");
isValidResponse = response.Equals(reject) || response.Equals(confirm);
if (!isValidResponse)
{
Console.Error.WriteLine($"Ugyldigt input: Du skal svare enten '{confirm}' eller '{reject}'");
}
} while (!isValidResponse);
return response.Equals(confirm);
}
string Prompt(string message)
{
Console.Write($"{message} ");
return Console.ReadLine();
}
}
}
Eksemplet viser hvordan man kan lave et “spil”, der går ud på at gætte det hemmelige tal.
using System;
namespace guessing_game
{
classProgram {
staticvoid Main(string[] args)
{
int min = 1;
if (args.Length > 1)
{
if (!int.TryParse(args[0], out min))
{
Console.Error.WriteLine("Invalid Argument: minimum must be an integer.");
return;
}
}
int max = 1000;
if (args.Length > 0)
{
string maxStr = (args.Length > 1)? args[1]: args[0];
if (!int.TryParse(maxStr, out max))
{
Console.Error.WriteLine("Invalid Argument: maximum must be an integer.");
return;
}
}
if (!(min < max))
{
Console.Error.WriteLine("Invalid Argument: minimum must be greater than maximum");
return;
}
GuessingGame game = new GuessingGame(min, max);
do {
game.play();
}
while (game.wantsRematch());
}
}
classGuessingGame {
privateint min;
privateint max;
public GuessingGame(int max)
{
this.min = 1;
this.max = max;
}
public GuessingGame(int min, int max)
{
this.min = min;
this.max = max;
}
publicbool wantsRematch()
{
Console.Write("Prøv igen? (Y/n): ");
string answer = Console.ReadLine();
bool isAfirmative = "y".Equals(answer.ToLower()) || "".Equals(answer);
return isAfirmative;
}
publicvoid play()
{
Random randomNumberGenerator = new Random();
int secretNumber = randomNumberGenerator.Next(min, max);
Console.WriteLine($"Gæt et tal mellem {min} og {max}");
bool done = false;
while (!done)
{
Console.Write("Skriv dit gæt: ");
var guessLine = Console.ReadLine();
bool isValidInteger = int.TryParse(guessLine, outint guess);
if (!isValidInteger)
{
Console.WriteLine("Dit gæt skal være et tal!");
continue;
}
bool isInValidRange = min <= guess && guess <= max;
if (!isInValidRange)
{
Console.WriteLine($"Dit gæt skal være melem {min} og {max}!");
continue;
}
if (guess == secretNumber)
{
Console.WriteLine($"Rigtigt! Flot gættet. Det hemmelige tal er {secretNumber}");
done = true;
}
else {
if (guess > secretNumber)
{
Console.WriteLine("Dit gæt er for højt");
}
if (guess < secretNumber)
{
Console.WriteLine("Dit gæt er for lavt");
}
Console.WriteLine("Gæt igen");
}
}
}
}
}
Dette eksempel viser hvordan man kan starte med at arbejde med React.
For at gøre indlæringskurven knap så stejl, benyttes Create React App til at oprette projektstrukturen, så man ikke selv skal sætte det hele op fra bunden.
Forudsætninger
For at kunne komme i gang kræves en fungerende installation af node.js.
Start med at kontrollere din version af node og npm, hvilket kan gøres med disse kommandoer.
node --version
npm --version
På min maskine giver de følgende output.
$ node --version
v19.8.1
$ npm --version
9.6.0
I dette eksempel er benyttet node version v11.15.0 og npm version 6.7.0
Opret projekt strukturen
Sørg for at din terminal er i den mappe hvor du ønsker at oprette dit projekt. Dernæst kan du oprette et projekt med denne kommando.
npx create-react-app my-app
Efter scriptet er afsluttte med success, burde du så kunne skifte bibliotek til det oprettede projekt.
cd my-app
Derefter kan projektet køres med kommandoen.
npm start
Dette kører din react app i udviklings-mode, hvilket bla. vil sige at der kører en server på maskine, så du kan se din app ved at åbne http://localhost:3000 i en browser.
Når du åbner siden burde du se noget i stil med det der er vist på figuren.
Screenshot af den kørende react app.
React Components
Prøv nu at lave din egen react komponent.
For at undgå at blande en masse forskellige komponenter sammen i laves den nye komponent i en separat fil.
Opret filen src/components/Clock.js og indsæt følgende.
Bemærk hvordan Clock komponenten importeres og indsættes i renderingen af App komponenten.
Styling
Vi har ikke brug for de css styles, der blev oprettet sammen med eksempel koden.
Men lidt plads omkring indholdet er ok.
Erstat derfor indholdet af src/App.css med dette.
.App {
padding: 1em;
}
State Hook
Det virker utilfredsstillende at bruge så meget energi på at lave et ur der ikke viser klokken.
Det kan løses med lidt javascript.
Man kan tilknytte state til enhver react komponent, så i dette tilfælde vil vi gøre det med tiden for uret ved at tilføje disse ændringer til src/components/Clock.js.
Nu vises klokken, men desværre skal man genindlæse siden for at få uret til at gå.
Effect Hook
For at få uret til at gå kan vi benytte en useEffect hook, der kaldes ved bestemte hændelser i komponentens livs-cyclus (life-cycle-events).
Vi er intereseret i at opdatere uret hvert sekund.
Hvilket kan opnås ved at ændre i Clock komponenten igen, så filen src/components/Clock.js ender med at se sådan ud.
Dette eksempel viser hvordan man kan integrere Firebase Firestore i en react app.
Det tager udgangspunkt i samme simple eksempel, som blev brugt i hotdog demoen.
Dog vil vi her nøjes med at læse data fra Firestore.
For at kunne benytte funktionaliteter fra firebase i vores react app, er det nødvendigt at
installere firebase SDK.
Dette kan klares med følgende kommando, som henter pakken fra npm og tilføjer den til package.json,
så vi har styr på projektets afhængigheder.
npm install firebase --save
Konfiguration af Firebase
Til dette eksempel er det ikke nødvendigt at oprette en ny firebase app, da den bruge samme Firestore database som denne demonstrationen fra tidligere.
Det er dog stadig nødvendigt at konfigurere firebase SDK til at kommunikere med den ønskede backend.
For at kunne benytte firebase på en nem måde i flere forskellige komponenter laves opsætning af og initialisering af firebase SDK i filen src/lib/Firebase.js, som så kan importeres af de komponenter der skal bruge firebase.
De konkrete settings, der skal bruges i dit projekt, finder du i firebase konsollen ved at gå ind i den app du vil bruge som backend.
Åben derefter “Settings” og “General”. Måske skal der oprettes et web endpoint, hvis du ikke allerede har gjort det.
For yderligere forklaringer kan denne intro sikkert bruges.
Læs fra Firestore
Nu er fundament på plads, så det er tid til at lave en react komponent, der henter data fra Firestore.
Det gøres i filen src/components/Hotdog.js, og koden er som vist herunder.
Først laves en fil med samme struktur som forventes at være i firestore databasen, når upload scriptet har kørt nogle gange.
Data strukturen laves i filen src/lib/fake_weather.js, med dette indhold.
importfirebasefrom"../lib/Firebase";
constTimestamp=firebase.firestore.Timestamp;
// helper for generating firestore timestamps
constt=timeString => Timestamp.fromDate(new Date(timeString));
// Fake data - could come from a database or similar
constdata= [
{
lastUpdate:t('2020-03-13T11:30:42'),
key:"dataIdOne",
windMeasurements: [
{ time:t('2020-03-13T10:20:42'), windSpeed:12, windDir:277 },
{ time:t('2020-03-13T11:30:42'), windSpeed:7, windDir:270 },
]
},
{
lastUpdate:t("2020-03-14T09:31:10"),
key:"dataIdTwo",
windMeasurements: [
{ time:t("2020-03-14T09:07:34"), windSpeed:17, windDir:179 },
{ time:t("2020-03-14T09:31:10"), windSpeed:25, windDir:183 },
{ time:t("2020-03-14T11:30:42"), windSpeed:12, windDir:180 },
{ time:t("2020-03-14T12:37:20"), windSpeed:23, windDir:171 },
]
},
];
exportdefaultdata;
Derefter laves en ny react komponent i src/components/Weather.js, som viser indholdet i browseren.
Bemærk hvordan vi først importeret datastrukturen fra filen src/lib/fake_weather.js, sender den ind i MeasurementList komponenten og itererer over indholdet af data i MeasurementList og MeasurementBody komponenterner.
Weather komponenten indsættes i src/App.js, således:
Hvis man gerne vil have tegnet en graf over noget data der er opsamlet, f.eks. fra en sensor, kan det være nyttigt at benytte et bibliotek af funktioner, som kan klare tegnearbejdet, så man ikke selv skal programmere det hele fra bunden.
Chart.js er et eksempel på sådan et bibliotek, der kan bruges til at få en graf frem på en hjemmeside.
Eksempel
I dette eksempel vises hvordan man kan indsætte en graf med to datasæt.
Først skal der laves et canvas element, som kan indeholde grafen.
Dernæst skal chart.js bibilioteket loades, hvilket kan gøres på flere måder som beskrevet i denne guide.
Derefter skal grafen laves hvilket gøres ved hjælp af javascript.
I dette eksempel er opsætning af grafen lavet i filen chart-demo.js, som så er hentet ind i html siden som vist ovenfor.
Herunder ses indholdet af filen chart-demo.js
varctx= document.getElementById("myChart").getContext("2d");
constxValues= [
"January",
"February",
"March",
"April",
"May",
"June",
"July"];
// generate some random values to plot
constyValues= [];
for (leti=0; i<xValues.length; i++) {
yValues.push(Math.random() *50);
}
varchart=newChart(ctx, {
// The type of chart we want to create
type:"line",
// The data for our dataset
data: {
labels:xValues,
datasets: [
{
label:"Random dataset",
borderColor:"rgb(255, 99, 132)",
data:yValues,
fill:false },
{
label:"Fixed dataset",
borderColor:"rgb(132, 99, 255)",
data: [20, 5, 5, 2, 24, 13, 25],
fill:false }
]
},
});
Bemærk at den ene serie består af faste værdier, og den anden er en række tilfældigt generede tal.
Demo
Her ses resultatet af anstrengelserne. Hvis du geninlæser siden gennereres der ny tilfældige værdier for den ene serie.
I dette eksempel forbindes der til Firestore via Node.js. Dvs. koden der skal afvikles på serveren kan skrives i javascript,
og vi kan derfor bruge viden om syntaks fra arbejdet med p5js.
Opsætning af Node projekt
For at kunne arbejde med firestore fra node.js, skal der laves en pakke / projekt at arbejde i.
Dette kan gøres ved at oprette en mappe til projektet og køre denne kommando for at generere en package.json fil.
npm init
Man bliver så bedt om at svare på en række spørgsmål, og ender med en struktur der ser nogenlunde sådan ud:
For at kunne benytte Firebase SDK skal vi have dette installeret, hvilket kan klares med kommandoen.
npm install firebase-admin --save
Vi får også brug for at arbejde med formatering af dato og tidspunkter, så derfor vælger vi at bruge biblioteket Moment.js for at lette arbejdet med disse.
Moment.js kan installeres med denne kommando.
npm install moment --save
Nu burde Firebase SDK og Moment.js være installeret og tilføjet som afhængigheder og package.json ser nu nogenlunde således ud.
Dette er et eksempel på en hvordan man kan lave et lille program, der indsætter en stump data i firestore.
Firestore upload eksempel
// Add the Firebase Admin SDK to Your Server
constfirebase=require("firebase-admin");
// Get the helper classes
FieldValue=firebase.firestore.FieldValue;
Timestamp=firebase.firestore.Timestamp;
constmoment=require("moment");
// Import crecential for the service account
constserviceAccount=require("./serviceAccountKey.json");
// Initialize the default app
constapp=firebase.initializeApp({
credential:firebase.credential.cert(serviceAccount),
databaseURL:"https://coldhawaiiweather.firebaseio.com"});
constdb=firebase.firestore();
// Helper - Generates random integer values
functiongetRandomInt(max, min) {
constdiff=max-min;
return Math.floor(Math.random() * Math.floor(diff) +min);
}
asyncfunctionstoreData() {
// Set time of update
constupdateTime=Timestamp.now();
// Generate some fake weather data
constwindData= {
time:updateTime,
windSpeed:getRandomInt(15, 3),
windDir:getRandomInt(0, 359),
};
// Document ID should be todays date
consttoday=moment(updateTime.toDate());
constdocPath=`weather/${today.format("YYYY-MM-DD")}`;
constdocRef=db.doc(docPath);
constdata= {
lastUpdate:updateTime,
windMeasurements:FieldValue.arrayUnion(windData),
};
constoptions= { merge:true }
awaitdocRef.set(data, options);
}
storeData();
Der skal bruges en såkaldt Service Account, for at kunne forbindes fra node til firebase.
Guiden forklarer, hvad du skal gøre for at oprette en service account til dit firebase projekt.
Disse oplysninger er private, så vær opmærksom på ikke at holde dem for dig selv.
For eksemplet virker skal oplysningerne om din Service Account oprettes i file serviceAccountKey.json.
Håndtering af private nøgler
For at undgå at sprede disse oplysninger er det en god ide at tilføje denne fil til listen over filer der skal ignoreres af git.
Dvs. at din .gitignore fil kunne se således ud.
Her er en stump tekst som kunne være et eksempel på noget input, hvor vi gerne vil finde alle telefonnumre.
Here is my number 123-456-7893.
Should you not be able to reach me there you can contact me at work (223)456-4305, or call my wife at 234.343.4521.
Opgaven med at finde telefonnumre kan i dette tilfælde løses ved at at bruge denne
regular expression eller regex.
[\(]\*\d{3}[)-\.]\d{3}[\.-]\d{4}
Her er eksempel på hvordan det kan benyttes i javascript.
constre=/\(?\d{3}[)-\.]\d{3}[\.-]\d{4}/g;
consttxt=`Here is my number 123-456-7893.
Should you not be able to reach me there you can contact me at work (223)456-4305, or call my wife at 234.343.4521.`;
constphoneNumbers=txt.match(re);
console.log(phoneNumbers);
En nem måde at komme i gang med at programmere til lego mindstorms EV3, er ved at bruge værktøjet
Makecode.
For at kunne benytte dette, kræves at din EV3 klods er
opdateret til firmware version 1.10E eller nyere.
Programmering
Du kan lave dit program ved hjælp af blok programmering, som du måske kender hvis du har arbejdet med Scratch, eller Applab.
Makecode udviklingsmiljøet i blokprogrammerings mode. Simulatoren ses i venstre side.
Udviklingsmiljøet indeholder mulighed for at simulere dit program direkte i din browser uden at overføre det til en fysisk EV3 brik.
Du kan se en grafisk visning af de enheder, der forventes at være tilsluttet til din EV3, på baggrund af indholdet af din kode.
Du kan også køre dit program som en simulering, og se om det opfører sig som forventet, ved at manipulere de virtuelle input i browseren.
Det kan dog være vanskeligt at simulere noget, hvor der er en fysisk kobling mellem input og output, som f.eks. en linjefølger, så du får også brug for at teste med en EV3 klods.
Som det også er tilfældet med Applab, kan du vælge at skrive dit program i javascript.
Her er et eksempel på et program der starter og stopper en motor, afhængigt af om en knap er trykket ind.
En af fordelene ved at bruge tekstbaseret kode som f.eks. javascript er, at du nemt kan klippe/klistre stumper af kode fra forskellige programmer, og sætte dem sammen til et nyt program.
Dette eksempel forudsætter at man har bygget en robot der kan køre ved at styre 2 separater motorer, en til hvert hjul.
Motorerne sidder på B og C udgangene.
Det kan være sværet at vurdere hvor hurtigt robotten kører med forskellige motorhastigheder. Derfor ender man ofte med at downloade talrige programmer til EV3 brick’en, for at teste forskellige motorhastigheder.
Endnu sværere bliver det at afgøre hvor skarp robottet drejer, når motorerne kører med forskellige hastigheder.
Derfor kan dette eksempel bruges til nemt at afprøve forskellige motorhastigheder.
Indstillinger for speed og turn ratio kan indstilles med knapperne på EV3 brick, og displayet viser de valgte indstillinger.
Eksempel på visning i displayet i simulatoren.
Blokprogrammering
Her er koden der bruges i eksemplet lavet med blokprogrammering.
Programmet lavet med blokprogrammering.
Javascript udgave
Den samme kode er her vist som javascript.
letturnRatio=0letspeed=0brick.buttonUp.onEvent(ButtonEvent.Pressed, function () {
speed+=10})
brick.buttonRight.onEvent(ButtonEvent.Pressed, function () {
turnRatio+=10})
brick.buttonLeft.onEvent(ButtonEvent.Pressed, function () {
turnRatio+=0-10})
brick.buttonDown.onEvent(ButtonEvent.Pressed, function () {
speed+=0-10})
brick.showString("Steer tester", 1)
brick.showString("Connect motors BC", 7)
brick.showString("up/down : speed", 8)
brick.showString("left/right : turn", 9)
forever(function () {
motors.largeBC.steer(turnRatio, speed)
brick.showValue("speed", speed, 2)
brick.showValue("turnRatio", turnRatio, 3)
brick.showValue("motor B speed", motors.largeB.speed(), 4)
brick.showValue("motor C speed", motors.largeC.speed(), 5)
pause(100)
})
Node gør det muligt at lave programmer der kan køres fra kommandolinien.
Her er et simpelt eksempel.
// this is a comment
/*
This
is
a
multiline
comment
*/// Erklæring af variabel
letname;
// Tildering af variable
name="Allan";
// Erklæring og tildeling på samme linje
letgreeting="Hej ";
// Iteration med for-løkke
for (leti=0; i<5; i++) {
letseparator='';
if (i<3) {
separator=', ';
} elseif (i<4) {
separator=' og ';
}
greeting+=name+separator;
}
// Udskrift til konsol
console.log(greeting);
// Matematiske beregninger
leta=5;
letb=3;
letnumber=a*b;
console.log(a+" gange "+b+" giver "+number);
// Betingelser og forgreninger
if (number>5) {
console.log("Det var et stort tal");
} else {
console.log("Ok tak");
}
// Definition af egne funktioner
functionmyAdd(a, b) {
returna+b;
}
result=myAdd(number, 34);
console.log(result);
// Brug af biblioteks funktion
letroot= Math.sqrt(result);
console.log(root);
console.log("Program afslutter");
Hvis du gemmer koden i filen node-demo.js, kan du eksekvere programmet med denne kommando.
node node-demo.js
Programmet burde så give følgende output i terminalen.
Hej Allan, Allan, Allan, Allan og Allan
5 gange 3 giver 15
Det var et stort tal
49
7
Program afslutter
Det kan være fordelagtigt at benytte npm pakken minimist, hvis der er brug for mere end de aller simpleste argumenter.
Det gør det langt nemmere at parse indholdet in i variabler, der kan benyttes i programmet.
Husk at installere minimist inden eksemplet køres, med kommandoen nmp install minimist
Yargs pakken
Er der brug for mere avanceret håndtering af argumenter på kommandolinjen, kan man benytte npm pakken yargs.
Det giver blandt andet mulighed for at tilknytte beskrivelser, typer og aliaser for de forskellige argumenter.
Her er et eksempel på håndtering af mere indviklede argumenter på kommandolinjen.
constyargs=require('yargs');
constargv=yargs .command('lyr', 'Tells whether an year is leap year or not', {
year: {
description:'the year to check for',
alias:'y',
type:'number',
}
})
.option('time', {
alias:'t',
description:'Tell the present Time',
type:'boolean',
})
.help()
.alias('help', 'h')
.argv;
if (argv.time) {
console.log('The current time is: ', new Date().toLocaleTimeString());
}
if (argv._.includes('lyr')) {
constyear=argv.year||new Date().getFullYear();
if (((year%4==0) && (year%100!=0)) || (year%400==0)) {
console.log(`${year} is a Leap Year`);
} else {
console.log(`${year} is NOT a Leap Year`);
}
}
console.log(argv);
For at kunne køre eksemplet kræver det at pakken yargs er installeret, hvilket kan gøres med kommandoen npm i yargs.
$ node cli-yargs-demo.js
{ _: [], '$0': 'cli-yargs-demo.js' }
$ node cli-yargs-demo.js -h
cli-yargs-demo.js [command]
Commands:
cli-yargs-demo.js lyr Tells whether an year is leap year or not
--version Show version number [boolean]
-t, --time Tell the present Time [boolean]
$ node cli-yargs-demo.js --version
1.0.0
$ node cli-yargs-demo.js --help lyr
cli-yargs-demo.js lyr
Options:
--version Show version number [boolean]
-t, --time Tell the present Time [boolean]
-h, --help Show help [boolean]
-y, --year the year to check for [number]
$ node cli-yargs-demo.js lyr
2021 is NOT a Leap Year
{ _: [ 'lyr' ], '$0': 'cli-yargs-demo.js' }
$ node cli-yargs-demo.js lyr --time
The current time is: 22.48.45
2021 is NOT a Leap Year
{ _: [ 'lyr' ], time: true, t: true, '$0': 'cli-yargs-demo.js' }
$ node cli-yargs-demo.js lyr --time -y 3000
The current time is: 22.48.59
3000 is NOT a Leap Year
{
_: [ 'lyr' ],
time: true,
t: true,
y: 3000,
year: 3000,
'$0': 'cli-yargs-demo.js'
}
Bemærk at YOUR_CLIENT_ID skal erstattes med det relevante id for din specifikke bot.
Du kan også få en korrekt URL genereret ved at gå ind under OAuth2 i menuen, og vælge bot under scopes.
Efter dette er forarbejdet gjort og du er klar til at starte på koden til din bot.
Først skal der laves et projekt så node kan finde ud af at køre programmet,
og har en package.json fil til at holde styr på projektet og afhængigheder af biblioteksmoduler.
Start med at lave en mappe, som kan indeholde dit projekt. Kald den f.eks. discord-bot.
I denne mappe skal du køre følgende kommando, for at oprette projekt filen package.json.
npm init
Udfyld passende værdier som svar på de spørgsmål programmet stiller.
Jeg foreslår at ændre din main fil til bot.js.
Dernæst har du mulighed for at installere de pakker, der skal bruges i projektet som afhængigheder.
Dette gøres med disse kommandoer.
Når programmet skal startes kan det gøres med kommandoen.
node bot.js
For at undgå at huske på hvilken fil der skal køres for at starte programmet,
kan man tilføje en start action i scripts sectionen i package.json.
Derefter kan du køre din bot, med denne kommando.
Hovedprogrammet til robotten benyttes sig af pakken discord.js.
Koden er forholdsvis kortfattet, og placeres i filen bot.js.
Bemærk hvordan kommandohåndteringen er uddelegeret til et andet modul.
Håndtering af de indkomne kommandoer er placeret i filen commands.js.
Bemærk hvordan der uden større ændringer kan tilføjes flere kommandoer til systemet.
constserverID=process.env.SERVERID;
constchannelID=process.env.CHANNELID;
consthelp= (msg, args) => {
constreply=`Avaliable commands are:
!help : Show this help message
!ping : Responds with pong
!hmm : Get a random response
!dog : Shows a dog
!toast : Get a quote from the toaster
`;
msg.channel.send(reply);
};
constdog=require('./commands/dog.js');
consthmm=require('./commands/hmm.js');
constping=require('./commands/ping.js');
consttoast=require('./commands/toast.js');
constcommands= { help, hmm, dog, ping, toast };
module.exports=asyncfunction (msg) {
// Only for this server and this channel
if (msg.guild.id===serverID&&msg.channel.id===channelID) {
// Handle command
lettokens=msg.content.split(' ');
letcommand=tokens.shift();
if (command.charAt(0) ==='!') {
command=command.substring(1);
// check that command exists
if (commands.hasOwnProperty(command)) {
commands[command](msg, tokens);
console.log(`got valid command, '${command}'`);
} else {
console.log(`got invalid command: '${command}', showing help`);
help(msg, tokens);
}
}
}
};
Brug af miljøvariabler
For at undgå at kode login tokens, server og kanal id’er ind i kildeteksten til programmet, benyttes pakken dotenv, således at disse kan hentes fra miljøvariable i stedet.
Konkrete værdier placeres i en hjælpefil sammen med koden i dette format. Filen skal navngives .env.
Pladsholderne skal erstattes med konkrete værdier fra den server bot’en skal være tilgængelig på.
Udelad private indstillinger i git
For at undgå utilsigtet deling af disse oplysninger er det en god ide at undlade at tilføje filen i git. Derfor tilføjes den til filen .gitignore, der f.eks kan se således ud.
node_modules/
.env
Discord specifikke værdier
De omtalte miljøvariabler skal som nævnt sættes til konkrete værdier, der afhænger af hvilken server/guild bot’en skal fungere sammen med.
For at kunne find de oplysninger, der skal indsættes i .env filen, er det nødvendigt at aktivere udviklertilstanden i discord.
Gå til brugerindstillinger > udseende
Scroll ned til avanceret
Tænd for udviklertilstand
Server ID kan nu tilgås sådan: højre-klik på serverens icon, vælg Kopiér ID i menuen.
Ligeledes kan Kanal ID fås sådan: højre-klik på tekst-kanalen, vælg Kopiér ID i menuen.
Bot Kommandoer
For at få en overskuelig struktur i koden er de enkelte kommandoer, som bot’en kan reagere på, implementeret i hver sin fil i mappen commands/.
!ping
Denne kommando svarer tilbage med pong.
Den er implementeret i filen commands/ping.js.
Bemærk at der ved at bruge funktionen reply(), automatisk insættes en @mention til den bruger der har aktiveret bot’en.
Denne kommando svarer tilbage med en besked tilfældigt udvalgt fra en liste med svarmuligheder.
Den er implementeret i filen commands/hmm.js.
constufoUrl='https://i.pinimg.com/originals/39/b2/34/39b234d75c67da28abdcb38b1b4cf649.png';
constreplies= [
ufoUrl,
'🤖 says go 🏄 and ⛷️',
'(╯°□°)╯︵ ┻━┻',
'¯\_(ツ)_/¯',
'Så er der 🍰'];
module.exports= (msg, args) => {
constindex= Math.floor(Math.random() *replies.length);
console.log(replies[index]);
msg.channel.send(replies[index]);
};
!dog
Denne kommando svarer tilbage med et tilfældigt udvalgt billede af en hund, som hentes fra et web-api.
Den er implementeret i filen commands/dog.js.
Kommandoen benytter pakken axios til at udføre kald til web-api’et.
Denne kommando svarer tilbage med et tilfældigt udvalgt citat, præsenteret af en toaster.
Kommandoen benytter pakken random-fortune til at generere tilfældigt udvalgte citater.
Desuden benyttes pakken cowsay til den visuelle præsentation af tekst output som ascii-art.
Kommandoen er implementeret i filen commands/toast.js.
Nu burde bot’en være funktionel. Den kan som tidligere nævnt startes med kommandoen npm start.
Husk at det er nødvendigt at genstarte programmet, hvis der laves ændringer i det.
Dette er et eksempel på en simpel web-server, lavet med node js og det ofte benyttede web framework express.
Ved at benytte pakker fra npm, kan man ret nemt lave systemet uden at skulle programmere alt op fra grunden.
Opsætning af projekt
Først skal der laves et projekt så node kan finde ud af at køre programmet, og har en package.json fil til at holde styr på projektet og afhængigheder af biblioteksmoduler.
Start med at lave en mappe, som kan indeholde dit projekt. Kald den f.eks. web-server.
I denne mappe skal du køre følgende kommando, for at oprette projekt filen package.json.
npm init
Udfyld passende værdier som svar på de spørgsmål programmet stiller.
Jeg foreslår at ændre din main fil til server.js.
Dernæst har du mulighed for at installere afhængigheder.
npm install express
Du burde nu have en package.json fil, der ser nogenlunde sådan ud:
Husk at genstarte serveren i terminalen, og genindlæse siden i browseren for at se ændringen.
Det bliver hurtigt unødvendigt besværligt at skrive html indholdet midt i server koden.
Derfor ændrer vi serveren til at læse det statiske indhold fra filer i en mappe på harddisken.
Herefter er det blot fantasien, der sætter grænser for indholdet.
Måske har du brug for at læse en kort introduktion til HTML eller CSS inden du går i gang.
Dette er eksempel viser hvordan man kan streame indholdet af et html canvas element (en p5js sketch) til en anden maskine.
Streaming delen håndteres af WebRTC, og etablering af forbindelsen håndteres ved hjælp af Socket.io.
Opsætning af projekt
Først skal der laves et projekt så node kan finde ud af at køre programmet, og har en package.json fil til at holde styr på projektet og afhængigheder af biblioteksmoduler.
Start med at lave en mappe, som kan indeholde dit projekt. Kald den f.eks. webrtc-server.
I denne mappe skal du køre følgende kommando, for at oprette projekt filen package.json.
npm init
Udfyld passende værdier som svar på de spørgsmål programmet stiller.
Jeg foreslår at ændre din main fil til server.js.
Dernæst har du mulighed for at installere disse afhængigheder.
npm install express
npm install socket.io
Indsæt en start action i script sektionen.
Du burde nu have en package.json fil, der ser nogenlunde sådan ud:
Bemærk de to pakker der er listet under afhængigheder.
Server
De statiske web-resourcer placeres i mappen public, og de gøres tilgængelige på samme måde som i eksemplet med den simple web server.
Derudover består serveren af en række endpoints, der håndterer socket.io beskeder.
Dette er nødvendigt for at kunne styre transmission af videostrømmen, da dette ikke er indbygget i WebRTC.
Når du ønsker at starte serveren, kan det gøres med kommandoen.
npm start
Afsender af video stream
For at have noget at sende afsted laves en sketch ved hjælp af p5js, der streames til modtagerne med WebRTC.
Tegn på canvas
Der laves en sketch, der tegner noget på det canvas element, som skal transmitteres.
I eksemplet tegnes blot en rød cirkel med sort omrids på musens position.
Dette laves i filen public/broadcast/sketch.js.
letstream;
functionsetup() {
// Capture the canvas content as a stream
constc=createCanvas(400, 400);
consthtmlCanvas=c.elt;
stream=htmlCanvas.captureStream();
gotStream(stream);
}
functiondraw() {
background(220);
// Draw a red circle at the position of the mouse
fill('red');
strokeWeight(5);
circle(mouseX, mouseY, 50);
}
Styring af video stream
For at kunne håndtere WebRTC forbindelsen kommunikeres med serveren via Socket.io.
Klient-delen der styrer afsendelsen laves i public/broadcast/webrtc.js
Javascript koden kan ikke stå alene.
For at kunne eksekvere den i browseren bliver den indsat på en simpel web side i filen public/broadcast/index.html.
Bemærk at filen /socket.io/socket.io.js genereres automatisk af serveren når socket.io pakken benyttes.
For at kunne tegne på canvas med p5js i public/broadcast/sketch.js er det nødvendigt at inkludere p5 biblioteksfilerne.
Disse er placeret i public/p5lib.
Dette eksempel viser hvordan man kan læse fra en serielport vha. node.js.
For at kunne køre eksemplet på en meningsfuld måde, kræver det at der er tilsluttet en enhed til serielporten på computeren.
Ydermere skal denne enhed sende beskeder afsted i json format, og porten skal være sat op med passende instillinger.
Først skal der laves et projekt så node kan finde ud af at køre programmet, og har en package.json fil til at holde styr på projektet og afhængigheder af biblioteksmoduler.
Start med at lave en mappe, som kan indeholde dit projekt. Kald den f.eks. serial-json.
I denne mappe skal du køre følgende kommando, for at oprette projekt filen package.json.
npm init -y
Dernæst har du mulighed for at installere disse afhængigheder.
Bemærk de to pakker der er listet under afhængigheder.
Derefter skal du oprette filen serial-json-read.js med følgende indhold.
constSerialPort=require("serialport");
constReadline=require("@serialport/parser-readline");
// Change the serial port to fit your serup
// on linux the device could look like this: "/dev/tty-usbserial1"
constserialPortName="COM6";
constportOptions= {
baudRate:115200};
constport=newSerialPort(serialPortName, portOptions);
// Open errors will be emitted as an error event
port.on('error', function(err) {
console.log('Error: ', err.message)
});
constparser=port.pipe(newReadline({ delimiter:"\r\n" }));
parser.on("data", doSomethingWithData);
// Callback function for processing the received data
functiondoSomethingWithData(data) {
console.log("--- Line received on serial port ------------");
console.log('Raw data: ', data);
// parse json data
constobj=JSON.parse(data);
// print object
console.log("Parsed object:");
console.log(obj);
console.log("Contents of object property:");
console.log(obj.tagId);
console.log("--- data processing done -------------------");
}
NB! Du skal ændre navnet på serielporten, så det passer med den port din Arduino er tilsluttet til.
Nu kan du køre eksemplet med denne kommando:
node serial-json-read.js
For at få noget meningsfuldt ud af at køre eksemplet skal der, som nævnt ovenfor, være en Arduino tilkoblet til en serielporten på computeren, og denne Arduino skal sende beskeder i det forventede json format.
Når dette ellers er opfyldt vil man se noget lignende dette output i terminalen.
$ node .\serial-json-read.js
--- Line received on serial port ------------
Raw data: {"waitTime":2307,"tagIdIndex":4,"tagId":"E2 00 00 1B 63 15 02 48 15 90 DB 2C"}
Parsed object:
{ waitTime: 2307,
tagIdIndex: 4,
tagId: 'E2 00 00 1B 63 15 02 48 15 90 DB 2C' }
Contents of object property:
E2 00 00 1B 63 15 02 48 15 90 DB 2C
--- data processing done -------------------
--- Line received on serial port ------------
Raw data: {"waitTime":3073,"tagIdIndex":3,"tagId":"E2 00 00 1B 63 15 02 48 17 20 DA F4"}
Parsed object:
{ waitTime: 3073,
tagIdIndex: 3,
tagId: 'E2 00 00 1B 63 15 02 48 17 20 DA F4' }
Contents of object property:
E2 00 00 1B 63 15 02 48 17 20 DA F4
--- data processing done -------------------
--- Line received on serial port ------------
Raw data: {"waitTime":6930,"tagIdIndex":2,"tagId":"E2 00 00 1B 63 15 02 48 17 00 DA F8"}
Parsed object:
{ waitTime: 6930,
tagIdIndex: 2,
tagId: 'E2 00 00 1B 63 15 02 48 17 00 DA F8' }
Contents of object property:
E2 00 00 1B 63 15 02 48 17 00 DA F8
--- data processing done -------------------
Dette eksempel viser hvordan man kan lave en webserver, der kan køre et eksempel hvor clienter på forskellige maskiner kan kommunikere via en web-socket.
Det er baseret på en videotutorial af Daniel Shiffman fra The Coding Train.
constexpress=require('express');
constapp=express();
constport=process.env.PORT||3000;
// Set up the server
// process.env.PORT is related to deploying on heroku
constserver=app.listen(port);
app.use(express.static(__dirname+'/public'));
console.log(`Server is running on http://localhost:${port}`);
constsocket=require('socket.io');
constio=socket(server);
io.sockets.on('connection', newConnection);
functionnewConnection(socket) {
console.log(`New connection ${socket.id}`);
socket.on('mouse', mouseMsg);
functionmouseMsg(data) {
socket.broadcast.emit('mouse', data);
// NB! send to all listeners (including source of incomming event)
// io.emit('some event, theDataToSend)
console.log(data);
}
}
public/sketch.js
constsocket=io();
functionsetup() {
createCanvas(400, 400);
background(0);
// Set up listener for incomming socket events
socket.on('mouse', newDrawing);
}
constlineWidth=10;
functionnewDrawing(data) {
// Draw some white circles with different colors
colorMode(RGB, 255);
fill(255, 0, 100);
noStroke();
ellipse(data.x, data.y, lineWidth, lineWidth);
}
functionmouseDragged() {
letcurrentNumX=mouseX;
letlowerBound=0;
letupperBoundX=width; //100;
letnormalizedX=norm(currentNumX, lowerBound, upperBoundX);
letcurrentNumY=mouseY;
letupperBoundY=height; //100;
letnormalizedY=norm(currentNumY, lowerBound, upperBoundY);
colorMode(HSB, 255);
letc=color(normalizedX*255, normalizedY*255, 255);
// Draw some white circles
fill(255);
noStroke();
ellipse(mouseX, mouseY, lineWidth, lineWidth);
constdata= {
x:mouseX,
y:mouseY,
};
console.log(`Sending ${data.x}${data.y} `);
socket.emit("mouse", data);
}
Serveren kan startes op med denne kommando.
npm run start
Web Scraping
Dette er et eksempel på en scraper, der finder alle links på en side, og skriver deres URL ud i konsollen.
Den benytter to pakker fra npm, så hele systemet ikke skal programmeres op fra grunden.
Pakken axios benyttes i eksemplet til at arbejde med http requests, og cheerio bruges til at parse html strukturen, der returneres fra serveren, og uddrage de data man er interesseret i at arbejde videre med i programmet.
Disse pakker skal installeres fra npm for at eksemplet kan eksekveres på din maskine.
Først skal der laves et projekt så node kan finde ud af at køre programmet, og har en package.json fil til at holde styr på projektet og afhængigheder af biblioteksmoduler.
Start med at lave en mappe, som kan indeholde dit projekt. Kald den f.eks. web-scraping.
I denne mappe skal du køre følgende kommando, for at oprette projekt filen package.json.
npm init -y
Dernæst har du mulighed for at installere disse afhængigheder.
Bemærk de to pakker der er listet under afhængigheder.
Derefter skal du oprette filen simple-scraper.js med følgende indhold.
constcheerio=require("cheerio");
constaxios=require("axios");
// Swap this for the url you want to scrape
consturl="https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States";
axios .get(url)
.then(response => {
let$=cheerio.load(response.data);
$("a").each(function(i, e) {
letlinks=$(e).attr("href");
console.log(links);
});
})
.catch(function(e) {
console.log(e);
});
Nu kan du køre eksemplet med denne kommando:
node simple-scraper.js
Async / await version
Her er et eksempel med samme funktionalitet, men hvor der bruges async / await syntaks i stedet for promise.then().
// import the library modules you installed from npm
constcheerio=require("cheerio");
constaxios=require("axios");
// Define the scraping algorithm
asyncfunctionrunScraper(url) {
try {
consthtml= (awaitaxios.get(url)).data;
const$=cheerio.load(html);
$("a").each(function(i, e) {
consturl=$(e).attr("href");
console.log(url);
});
} catch (error) {
console.log("error: "+error);
}
}
// Swap this for the url you want to scrape
consturl="https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States";
// run the scraper by calling the function
runScraper(url);
Materiale
NPM pakker
Her er en række pakker, der kan bruges til at lave scraping af websider.
Denne tutorial er et eksempel på hvordan man kan hente en side med links til amerikanske præsidenter fra wikipedia, og ved at følge disse links hente de enkelte præsidenters navne og fødselsdage.
fill() vælger fyldfarven. Denne har kun betydning for lukkede figurer som f.eks. firkanter og cirkler.
Du kan bruge color() til at oprette en farve, og gemme værdien i en variabel, så den nemt kan genbruges forskellige steder i programmet. Her er et eksempel:
Dette eksempel viser hvordan man opretter et lærred og tegner figurer på skærmen.
Der benyttes variabler og musemarkørens position til at styre hvor på skærmen der tegnes.
functionsetup() {
createCanvas(windowWidth, windowHeight);
}
functiondraw() {
background(220);
fill(255);
// variable declaration
letx;
// variable assignment
x=mouseX;
// declaration and assignment in one line
lety=mouseY;
circle(x, y, 150);
// assign a new value to x
x=200;
ellipse(x, y, 80, 40);
fill(255, 0, 0);
circle(width, height, 120);
letcx=width/2;
letcy=height/2;
circle(cx, cy, 50);
}
Eksemplet tegner en mængde cirkler på skærmen, der falder mod bunder af skærmen som en slags regn.
Der defineres en funktion der kan ændre cirklernes egenskaber: position og hastighed.
Der benyttes arrays til at kan holde styr de mange cirklers individuelle egenskaber.
Dette er et eksempel på, hvordan man kan benytte p5.Vector klassen til at simulere kollision mellem 2 cirkler.
Den resulterende hastighedsvektor efter kollisionen beregnes ved at benytte reflektion om normalvektoren til de sammenstødende cirkelperiferier.
Reflektionen kan beregnes ved hjælp af formlen
$$\vec r = \vec d - 2 (\vec d \cdot \vec n) \vec n$$
hvor
\( \vec d \) er den indkommende vektor,
\( \vec n = \frac{\vec n}{|\vec n|}\) er en normaliseret normal-vektor til cirkelperiferien,
og \( \vec r \) er reflektionen af \( \vec d \) omkring \( \vec n \).
\(\vec n\) er en normal vektor til overfladen, og denne er normaliseret dvs.
$$\vec n = \frac{\vec n}{|\vec n|}$$
Projektionen \(\vec p\) af \(\vec d\) på overfladens normalvektor \(\vec n\) findes ved hjælp af skalar produktet
$$\vec p = (\vec d \cdot \vec n)\vec n$$
Vektoren \(\vec e\) bruges som hjælp i beregningen
$$\vec e = \vec d - \vec p$$
Da indfaldsvinkel og udfaldsvinkel er ens, og desuden er størrelsen af den indgående vektor og reflektionen ens \(|\vec d| = |\vec r|\) fås
$$\vec r = -\vec d + 2\vec e$$
Derefter kan formlerne kombineres
$$
\begin{aligned}
\vec r &= -\vec d + 2(\vec d - \vec p) \\
&= -\vec d + 2(\vec d - (\vec d \cdot \vec n)\vec n) \\
&= -\vec d + 2\vec d - 2(\vec d \cdot \vec n)\vec n \\
&= \vec d - 2(\vec d \cdot \vec n)\vec n
\end{aligned}
$$
Struktur af programmet
Eksemplet består er opdelt i 3 filer, som er inkluderet i html filen således:
Her er eksemplet, der printer tal fra 0 til 9, lavet med en while løkke.
leti=0while(i<10){
console.log(i);
i++;
}
Eksempel med Løkke
Dette eksempel viser hvordan man kan benytte funktionen map(), til at lave lineær interpolation.
Endepunkternes position kan varieres ved at ændre musemarkørens x-position.
De små cirkler generes i for-løkken, og antallet af cirkler styres af musens y-position.
functionsetup() {
createCanvas(windowWidth, windowHeight);
fill(133, 18, 9);
stroke(209, 52, 40)
strokeWeight(3)
}
functiondraw() {
background(70);
// Define tilt based on mouse horizontal position
constyRange=height/4constdeltaY=map(mouseX, 0, width, -yRange, yRange, true)
// Define control points
constax=50constay=height/2-deltaYconstbx=width-axconstby=height/2+deltaY// Draw large cirles at control poins
constdiameter=50circle(ax, ay, diameter)
circle(bx, by, diameter)
// Define number of circles based on mouse vertical position
letn=map(mouseY, 0, height, 30, 2, true)
n=ceil(n)
// Draw circles using a loop
for (leti=0; i<=n; i++) {
constx=map(i, 0, n, ax, bx)
consty=map(i, 0, n, ay, by)
circle(x, y, diameter/2)
}
}
I dette eksempel demonstreres hvordan man kan benytte forgreninger og boolske udtryk til at ændre bevægelsesretningen på en cirkel, så det minder om en bold, der hopper når den rammer siderne.
// Lav en variabel og kald den x
// giv x værdien 200
letx=200;
// opret flere variabler
letxSpeed=5;
lety=200;
letySpeed=3;
letc;
letcFill;
// Definer en funktion der kan ændre fyld og stregfarve
functionchangeColor() {
c=color(random(255), random(255), random(255));
cFill=color(random(255), random(255), random(255));
strokeWeight(10);
stroke(c);
fill(cFill);
}
functionsetup() {
createCanvas(windowWidth, windowHeight);
constspeedScale=128xSpeed=windowWidth/speedScaleySpeed=windowHeight/speedScalechangeColor();
}
functiondraw() {
background(c);
rect(0, 0, width, height);
// opret variabler til radius og diameter
letr=60;
letd=r*2;
// tegn en cirkel med centrum i (x, y) og diameter d
circle(x, y, d);
// hvis x er større end bredden af lærredet
// så sæt xSpeed til -xSpeed
letisPastRightSide=width<x+rif (isPastRightSide) {
changeColor();
xSpeed=-xSpeed;
}
letisPastLeftSide=0>x-rif (isPastLeftSide) {
changeColor();
xSpeed=-xSpeed;
}
letisBelowBottom=height<y+rletisAboveTop=0>y-rif (isBelowBottom||isAboveTop) {
changeColor();
ySpeed=-ySpeed;
}
x=x+xSpeed;
// samme som
// y = y + ySpeed;
y+=ySpeed;
}
Dette eksempel tegner cirkler med tilfældigt valgt fyldfarve.
Når venstre museknap er trykket ned skiftes til fyldfarver i tilfældige gråtoner.
I eksemplet viser hvordan man kan bruge en betingelse (museknappen er trykket ned), og en en forgrening (if-sætning) til at få programmet til at ændre opførsel.
Status for de enkelte input registreres i variablen state, som er et objekt med de nødvendige attributter.
I eksemplet er håndteringen af venstre piletast fremhævet.
Denne måde at håndtere input bevirker at figuren kan bevæges diagonalt, ved at trykke på to taster samtidigt.
Desuden er bevægelsen uafhængig af repeat rate i keyboard indstillingerne.
Dette er et eksempel på hvordan man man håndtere flere samtidige berørings hændelser, f.eks. som knapper i et mobil spil, der kræver flere samtidige inputs.
Bemærk at det er nødvendigt med en smule styling via CSS for at få eksemplet til at virke.
Derfor er der tilføjet en klasse til button elementerne, og denne regel er tilføjet til stylesheet for siden.
.noselect {
-webkit-touch-callout: none; /* iOS Safari */-webkit-user-select: none; /* Safari */-khtml-user-select: none; /* Konqueror HTML */-moz-user-select: none; /* Old versions of Firefox */-ms-user-select: none; /* Internet Explorer/Edge */user-select: none; /* Non-prefixed version, currently
supported by Chrome, Opera and Firefox */}
Denne demonstration er et eksempel på hvordan man kan bruge klasser og objekter til at strukturere koden.
I eksemplet tegnes nogle hoppende tændstikmænd med forskellige egenskaber.
Filen sketch.js indeholder den sædvanlige struktur for et program skrevet i p5js.
I setup() oprettes et lærred, og der oprettes tre objekter af klassen stickman.
Bemærk at de bliver initialiseret med forskellige egenskaber via deres constructor.
I draw() kaldes metoderne render() og update() på begge de to StickMan objekter.
Derudover er metoden mouseClicked() implementeret, og denne sørger for at kalde metoden jump på de to stickman objekter,
med den effekt at de begge hopper når der klikkes med musen.
Skriv et program, der finder positionen af det første mellemrum i en streng.
Skriv et program, der fjerner det første ord i en sætning (indtil første mellemrum).
Skriv et program, der tæller antallet af mellemrum i en tekst.
Skriv et program, der fjerner den første forekomst af ordet “måske” fra en tekst. Ændr derefter programmet, så det fjerner alle forekomster af ordet (brug f.eks. en løkke).
Skriv et program, der finder og fjerner alle forekomster af ordet “måske” fra en tekst, uanset om det er skrevet med store eller små bogstaver.
Skriv et program, der undersøger, om en tekst er et palindrom, dvs. med samme stavning forfra og bagfra (som f.eks. “regninger”, “russerdressur”, “vær dog god ræv”).
Udvid programmet til at tage højde for store/små bogstaver, tegnsætning og mellemrum, sådan at de følgende palindromer også genkendes: “Selmas lakserøde garagedøre skal samles” og “Åge lo, da baronesse Nora bad Ole gå”.
Det skal være muligt at få hjælp til interaktion med spillet (e.g syntaks for gyldige kommandoer).
Det skal være muligt at starte spillet.
Computeren skal generere en hemmelig kode.
Koden skal bestå af 4 ud af mulige 6 forskellige farver (eller tegn).
Der må gerne forekomme gentagelser af den samme farve i koden.
Det skal være muligt at afgive et gæt.
Bot’en skal give tilbagemelding på hvert gæt bestående af
Antal modtagne gæt.
Gentagelse af det afgivne gæt
Angivelse af antal rigtigt placerede farver
Angivelse af antal rigtige farver
Hvis man gætter koden, skal der gives tilbagemelding om at man har vundet.
Hvis koden ikke er gættet efter 10 forsøg, har computeren vundet.
Dette skal annonceres i svaret, og den hemmelige kode afsløres, så man kan kontrollere at alt er gået retfærdigt til.
Ekstra ideer og nice to have funktioner
Man skal have mulighed for at give op, få vist koden og starte forfra.
Hold styr på hvor mange gange parterne har vundet.
Her er et eksempel på hvordan interaktionen med programmet kan laves.
!mm help
Welcome to Mastermind!
Guess the secrect code.
Code consist of 4 numbers in the range 1 through 6.
Show this help message
!mm
or
!mm help
Start / restart game
!mm start
Make a guess for the code 1234
!mm g 1234
The game response shows your guesses and provides feedback
o : code contains number
x : correct placement of number
!mm start
Starting new mastermind game, generated secrect code.
Lav et kommandobaseret program der gør det muligt at spille “Sten-saks-papir” mod computeren.
Programmet skal kunne tage mod simple input kommandoer, tolke betydningen, og vise dem i output som en “tegning” lavet med nogle ascii symboler, f.eks.:
Input
Betydning
Symbol
r
sten
0
p
papir
—
s
saks
>8
Programmet skal også holde styr på hvor mange gange parterne har vundet.
Hvordan dette håndteres internt i programmet er op til programmøren at finde en god løsning på.
Her er et eksempel på hvordan output kunne se ud.
rock-paper-scissor $ .\bin\Debug\netcoreapp3.1\rock-paper-scissor.exe
Welcome to RPS!
Make your selection!
Rock(r), Paper (p), Scissor (s)!?
Result: >8 vs 0 => Computer WINS
Score: Player(0) vs Computer(1)
Press any key to continue or ESC to quit
Make your selection!
Rock(r), Paper (p), Scissor (s)!?
Result: 0 vs 0 => TIE
Score: Player(0) vs Computer(1)
Press any key to continue or ESC to quit
Make your selection!
Rock(r), Paper (p), Scissor (s)!?
Result: --- vs 0 => Player WINS
Score: Player(1) vs Computer(1)
Press any key to continue or ESC to quit
Opgave: RPS Discord Bot
Denne opgave går ud på at lave en discord bot som man kan dyste mod i “sten-saks-papir”.
Lav en ny kommando i din discord bot, der gør det muligt at spille “Sten-Saks-Papir” mod computeren.
Hold styr på hvor mange gange parterne har vundet.
Her er et eksempel på hvordan interaktionen med programmet kan laves.
!rps scissor
Result: >8 vs 0 => Computer WINS
Score: Player(0) vs Computer(1)
!rps rock
Result: 0 vs 0 => TIE
Score: Player(0) vs Computer(1)
!rps paper
Result: --- vs 0 => Player WINS
Score: Player(1) vs Computer(1)
Shuffle & Sort
I dette projekt arbejdes med algoritmer til blanding og sortering.
Opgave: Shuffle
Der skal laves et program, der kan blande en række heltal i et array.
Lav en beskrivelse af hvordan algoritmen fungerer.
Din beskrivelse skal indeholde et flow chart.
Lav en implementation i C#. Hint: Du kan benytte koden i ShuffleProgram.cs som udgangspunkt.
Hint: Du får sikkert brug for at generere tilfældige tal, hvilket kan gøres med System.Random.Next().
Lav manuel simulering af algoritmen. Den kan simuleres ved at afvikle game of life på et stykke papir eller en ternet spilleplade.
Brug perler / lego / eller andre små genstande til at symbolisere levende celler.
Lav derefter din egen version af version i p5.js.
Som inspiration kan du bruge denne videopræsentation.
Lav et program der kan lave tekster om til “volapykdansk” ala nedenstående.
Reglerne for konvertering af teksten er beskrevet i det følgende.
Iøfgle en uerndsøeglse på Cmabrigde Uinvertisy bteyedr det ikke ngoet i hivklen rkkæefgløe bgotsavrnee såtr i et ord, det eestne vgitgie er at det frøtse og sditse bgsoatv i odret er på de rtete psadler. Rsteen kan lngie vloaypk, men du vil sdtaig vræe i snatd til at lsæe det. Det er frodi den mnenesekilge hejrne ikke lesær hevrt bgotasv, men odret som en hhleed.
Man skal kunne låse den op ved hjælp af indlæsning af farver i en bestemt sekvens.
Indlæsningen af hver farve bekræftes med et tryk på den tilsluttede knap.
Man skal kunne ændre på farverne og længden af sekvensen uden at lave en større modifikation af programmet (Hint: Benyt f.eks et array).
Forslag til kodestruktur
Denne struktur kan bruges som skabelon til at løse opgaven.
letcode:number[] = []
letisCorrect=falsefunctionopenGate() {
// TODO Scan code and determine if it matches correct
}
sensors.color2.setMode(ColorSensorMode.Color)
motors.largeC.setBrake(true)
code= [ColorSensorColor.Red, ColorSensorColor.Green, ColorSensorColor.Blue, ColorSensorColor.Brown]
forever(function () {
// Indicate program is running
brick.showImage(images.eyesBlackEye)
isCorrect=true// TODO Scan code and determine if it matches correct sequence
if (isCorrect) {
openGate()
} else {
// TODO indicate wrong code
}
})
I dette projekt handler det om at styre lyset i en række multifarvede LED’er, som vist på billedet.
Tanken er at bygge videre på tre kode eksempler, så enkeltdelene kan bruges til at lave et system der kan styre lyset i LED arrayet fra en browser, f.eks. i en mobiltelefon.
Opgaven
Lav et system, der kan styre en række Neopixel LED’er fra et webinterface i en browser.
Systemet tænkes at bestå af følgende komponenter:
En Arduino med tilsluttet array af NeoPixel LEDs.
En server lavet i node.js, der håndterer kommandoer fra brugerens browser.
Et simpelt node modul, der håndterer kommunikation med Arduino via serielporten.
Brugerinterface baseret på HTML, CSS og Javascript, der via websockets sender beskeder til serveren om styring af LED lys.
Klient / server kommunikation. Dette eksempel viser hvordan man kan kommunikere mellem klienter via Socket.io, ved at lave en server i node.js.
Arduino JSON commands. Viser hvordan man kan håndtere udveksling af beskeder mellem computer og Arduino.
Serial Port Kommunikation.
Dette eksempel viser hvordan man kan sende og modtage json beskeder via seriel porten, ved hjælp af et script i node.js.
Teknikken kan bruges til kommunikation mellem computer og Arduino ved hjælp af simple kommandoer.
Lav et it system der gør det muligt at se data fra en vejrstatation på en nem og overskuelig måde.
Løsningen skal virke på mobil, tablet og computeren.
Opgaven løses i grupper.
Der skal laves en kravspecifikation.
I skal bruge et projetstyrings værktøj til at styre jeres opgaver, f.eks. Trello eller Milanote.
“Oversæt” krav fra kravspecifikationen til konkrete opgaver på jeres trello board.
Løsningen skal designes så den ikke belaster vejrstationen unødigt når den bruges samtidig af mange brugere, f.eks. ved at sende unødigt mange forespørgsler.
Eksempel på skitse af tændstikmand. Bemærk at y-aksen regnes positivt fra toppen og ned.
Opgave: Lav en tændstikmand
Opgaven går ud på at eksperimentere med computergrafik.
Ved hjælp af p5js skal du lave et program der benytter forskellige tegne funktioner, til at generere en “tændstikmand”.
Han skal som minimum bestå af:
hoved
mund
øjne
krop
arme
ben
Ekstra: til den “kvikke elev”; få din tændstikmand til at ændre udseende dynamisk
f.eks. vha. museinput, knaptryk
hovedets størrelse
blinke med øjnene
ændre farver
Du kan med fordel tegne en skitse først, så du nemmere kan holde styr på koordinater.
Når man skal arbejde med programering og softwareudvikling, er det ofte nyttigt at kunne anvende et kommandolinjeinterface, til nogle basale opgaver.
Nyttige kommandoer
pwd (path to working directory) viser stien til den mappe man befinder sig i.
cd (change directory) skifter til den mappe, der angives som argument.
ls (list directory) viser indholdet i en mappe.
mkdir (make directory) opretter en en mappe med det angivne navn.
rmdir (remove directory) sletter en mappe.
cat udskriver indholdet af en fil til skærmen.
echo udskriver argumentet til skærmen
date udskriver det aktuelle klokkeslet til skærmen
touch opretter en fil med det angivne navn. Hvis filen allerede findes, opdateres ændringstidspunktet til det aktuelle klokkeslet.
^C (ctrl+c) afbryder et kørende program / kommando uden at vente på det afslutter af sig selv. Dette er nyttigt hvis man f.eks. kommer til at at lave en uendelig løkke.
Omdirigering af output
Her er et eksempel på hvordan man kan bruge omdirigering af output fra kommandoer (linjer der starter med ‘$’ er prompten hvor kommandoen skrives).
$ date > my-file.txt
$ cat .\my-file.txt
Fri Sep 24 11:20:58 Rom, sommertid 2021 $ date >> my-file.txt
$ date > my-file.txt
$ echo "test af append to file" >> .\my-file.txt
$ date >> my-file.txt
$ cat .\my-file.txt
Fri Sep 24 11:21:19 Rom, sommertid 2021 test af append to file
Fri Sep 24 11:21:58 Rom, sommertid 2021 $
Git
Her er nogle få kommandoer til håndtering af git depoter (repositories).
Opsætning af git
git config --global user.name "FIRST_NAME LAST_NAME" sætter navnet på brugeren af git globalt for alle depoter på maskinen.
git config --global user.email "user@example.com" sætter email adressen på brugeren af git globalt for alle depoter på maskinen.
Initialisering og Kloning
git init opretter et depot.
git clone [URL] opretter en klon af et depot fra f.eks. github. URL er der hvor depotet befinder sig.
Dagligt arbejde med git
git status viser status for det lokale depot.
git add . tilføjer alle ændringer i arbejdsområdet rekursivt.
git commit -m "COMMIT MESSAGE HERE" committer ind i det lokale depot.
git pull henter og fletter ændringer ind fra en remote.
git push skubber ændringer ud til en remote.
git fetch henter ændringer fra remote uden at flette dem sammen automatisk.
git merge [BRANCH] fletter ændringer fra BRANCH ind i den aktuelle gren (branch).
Iterativ udvikling
Code Editors and IDE's
For at kunne arbejde med HTML, CSS, javascript og andre tekst filer har vi brug for en god editor.
Husk at vælge alle tilvalg i additional tasks under installationen af VS Code, som vist på billedet. Det gør det nemmere at åbne hele mapper som projekter, i stedet for enkelte filer.
Husk at alle krydser under installationen.
Her finder du information om Visual Studio Code og en kort video intro.
Git installation. Vælg den der passer til dit operativ system. På mac er git sikkert allerede installeret. Bruger du linux, ved du formentlig selv hvordan du installerer pakker, der passer til din distribution.
Den følgende illustration viser hvordan forskellige kommandoer påvirker komponenterne i git systemet.
Denne figur illustrer hvordan de enkelte filer skifter tilstand når man arbejder med git.
Samarbejde via github
For at kunne samarbejde om den samme kodebase er det nødvendigt med en smule setup for at komme igang.
Hvis vi tager udgangspunkt i et nyt projekt kræves denne opsætning.
opret et repository i github
lave en klon på den/de computere der skal deltage.
Ejeren af “repository” skal tilføje de andre bruger som samarbejdspartnere (Collaborators)
Samarbejdspartnere skal acceptere invitationen (typisk via et link i en email)
Efter accept af samabejdsinvitation kan de inviterede brugere skubbe kode ind i projektet.
Samarbejde via Git
Her er en visualisering af opsætningen med to samarbejdende udviklere.
Basale kommandoer
Ønsker du at bruge git fra en kommandoprompt er her en liste med basale kommandoer
Github pages
Her finder du en guide til at komme igang med Github pages.
Denne videoguide forklarer hvordan man kommer igang med at bruge github pages til at hoste sin webside.
Konfliktløsning
Når man er flere der samarbejder om samme kode kompleks opstår der uungåeligt det at man på et tidspunkt kommer til at ændre i en fil , der allerede er blevet ændret af en anden person.
Derfor er det nødvendigt at vide hvordan man løser disse konflikter i git, så man kan komme videre med arbejdet.
Konfliktløsningsscenariet kan illustreres med et eksempel i et sekvensdiagram.
Konfliktløsning i git er også beskrevet i denne video.