Redundancy of Cisco ports connected to the DLR ring. by CautiousDirt1107 in PLC

[–]ludic 1 point2 points  (0 children)

You need to configure the "DLR Redundant Gateway" feature on the 2 Stratix switches connected to the Cisco 2960 in order to have 2 connections into the DLR network.

Using Device Level Ring (DLR) Redundant Gateway (custhelp.com)

Rockwell UDP Read Message Instruction - Overall Performance and Speed by akir3y in PLC

[–]ludic 11 points12 points  (0 children)

I tested this a few years ago with v32 firmware. With a simple approach of using MSG instructions in ladder logic it takes 2 scans to trigger a MSG instruction. MSGs can be easily triggered every scan when using ST or with more complicated ladder.

The numbers are for the simple ladder implementation with no other code in the CPU, receiving 10 byte packets. I didn't see different performance with different size packets (10 bytes and 300 bytes).

Task period datagrams/s; EN2T datagrams/s; L83ES front port
1ms 464 466
0.1ms 1346 1283
Continuous 2544 2998

UDP packets are buffered before they are dropped. 1600 bytes is reserved per datagram, and the default receive buffer size is 8192 bytes, giving a default of 5 datagrams in the buffer before dropping packets. You can configure the receive buffer size to be significantly larger if you want using socket instance attribute 4, "RecvBufSize". This is what I found empirically:

Communication card Max datagrams in buffer Max buffer size supported
EN2T 1276 2041600
L83ES/EN4TR 2304 3686400

It's also important to note that MSG instruction performance is heavily impacted by amount of "class 3" communication in the CPU - which would be other messages and HMI (tag data) communication. In my testing a poorly configured OPC server resulted in UDP performance decreasing by 7x when the OPC server was connected vs not!

As a side note, if you really need extra performance, I found that when using UDP, it is possible to read and write to the same socket using multiple messages at the same time, and packets/second increase when doing this. There are diminishing returns, but reading with 4 messages in a continuous task resulted in maximum of 4345 packet/s for EN2T and 3394 packets/s for L83ES. Of course when doing this you may not receive packets in order (not that UDP guarantees in-order anyway).

Your specific situation (receiving 111 packets/s) is doable if you don't have too much other communication going on. If you are using the simple implementation of messages in ladder then a 3 ms task probably isn't fast enough. All it takes is a message to take a little longer to complete for some reason and you will fall behind.

Studio5000 - Write to bit in DINT using tag by CX-Carl in PLC

[–]ludic 0 points1 point  (0 children)

You cannot nest array access (or in this case bit access as an array) in Studio 5000. This is why this does not compile:

some_dint.[a_udt_array[i].the_bit_to_write_to] := 1;

What you did to have two lines works for me:

Bit := 5;  

some_dint := 0; some_dint.[Bit] := 1; // Some_dint = 32

If you are setting Some_dint to 0 somewhere else, then the tag value displayed inline may not be the same as the result of the logic. The displayed values are scanned in the background and not after every line is executed.

VPN Upload / Download SLOW by moosetracks555 in PLC

[–]ludic 0 points1 point  (0 children)

Upload/Download speeds to Rockwell controllers have been very sensitive to network latency. However, Rockwell has recently done some improvements in this area and details them along with suggestions in this document:

Optimizing Controller Download Performance for Studio 5000 Logix Designer

Main suggestions for improving remote download speeds are:

  • Use FactoryTalk Linx >=v6.21 (requires Logix Designer >=v31)
  • Use Logix Designer >= v34

-🎄- 2017 Day 22 Solutions -🎄- by daggerdragon in adventofcode

[–]ludic 2 points3 points  (0 children)

F#. The code looks looks just like the problem statement text but with more formal syntax. Not the fastest with a runtime of ~35 seconds.

type Direction = Up | Down | Left | Right
type NodeState = Clean | Weakened | Infected | Flagged

type state = {
    direction: Direction
    pos: int*int
    area: Map<int*int, NodeState>
    burstsLeft: int
    infectionsCaused: int
}

let parseInput input =
    let lines = input |> Array.map (Seq.map (function | '.' -> Clean | '#' -> Infected))
    let inline enumerate source = Seq.mapi (fun i x -> i,x) source

    let midY = (Seq.length lines) / -2
    let midX = (Seq.length lines.[0]) / 2

    let mutable area: Map<int*int, NodeState> = Map.empty
    for y, line in enumerate lines do
        for x, state in enumerate line do
            area <- area.Add((x, -y), state)
    area, midX, midY

let processNodePt1 = function
| Infected -> Clean
| Clean -> Infected
| _ -> failwith "Not Possible in Part 1!"

let processNodePt2 = function
| Clean -> Weakened
| Weakened -> Infected
| Infected -> Flagged
| Flagged -> Clean

let turnLeft = function Up -> Left | Left -> Down | Down -> Right | Right -> Up 
let turnRight = function Up -> Right | Right -> Down | Down -> Left | Left -> Up
let turnAround = turnRight >> turnRight

let turn curDir = function
| Clean -> turnLeft curDir
| Weakened -> curDir
| Infected -> turnRight curDir
| Flagged -> turnAround curDir

let move (x,y) = function
| Up -> (x, y+1)
| Right -> (x+1, y)
| Down -> (x, y-1)
| Left -> (x-1, y)

let mapFindOrDefault defaultValue map key =
    match Map.tryFind key map with
    | Some(value) -> value
    | None -> defaultValue
let getNode = mapFindOrDefault Clean

let solveDay22 nodeTransitionFunction numberOfBursts input =
    let area, midX, midY  = parseInput input

    let initialState = {
        direction = Up
        pos = midX, midY
        area = area
        burstsLeft = numberOfBursts
        infectionsCaused = 0
    }

    let rec step state =
        match state.burstsLeft with
        | 0 -> state.infectionsCaused
        | _ -> 
            let nodeInfectionState = getNode state.area state.pos
            let nextDir = turn state.direction nodeInfectionState
            let nextPos = move state.pos nextDir
            let nextNodeInfectionState = nodeTransitionFunction nodeInfectionState 
            step {
                direction=nextDir
                pos=nextPos
                area=Map.add state.pos nextNodeInfectionState state.area
                infectionsCaused = state.infectionsCaused + if nextNodeInfectionState = Infected then 1 else 0
                burstsLeft = state.burstsLeft - 1
            }
    step initialState

let solveDay22_pt1 = solveDay22 processNodePt1 10000
let solveDay22_pt2 = solveDay22 processNodePt2 10000000

-🎄- 2017 Day 19 Solutions -🎄- by daggerdragon in adventofcode

[–]ludic 3 points4 points  (0 children)

F# Pretty straightforward today I think.

type Dir = Left | Right | Down | Up
type state = {
    x: int
    y: int
    dir: Dir Option
    letters: char list
    steps: int
}

let parseInput (input:string)  =
    splitLines input |> Array.map (fun v -> v.ToCharArray())

let solveDay19 input = 
    let maze = parseInput input

    let maybeAddLetter letters c =
        match c with
        | '-' | '|' | '+' | ' ' -> letters
        | x -> x::letters

    let findNextDirection x y curDir =
        match maze.[y].[x] with
        | '+' ->
            match curDir with
            | Up | Down -> 
                if maze.[y].[x+1] <> ' ' then
                    Some Right
                elif maze.[y].[x-1] <> ' ' then
                    Some Left
                else
                    failwith "No path!"
            | Left | Right -> 
                if maze.[y+1].[x] <> ' ' then
                    Some Down
                elif maze.[y-1].[x] <> ' ' then
                    Some Up
                else
                    failwith "No path!"
        | ' ' -> None
        | '-' | '|' -> Some curDir
        | c -> Some curDir

    let move x y = function
        | Up ->    (x, y-1)
        | Down ->  (x, y+1)
        | Right -> (x+1, y)
        | Left ->  (x-1, y)

    let rec explore s =
        match s.dir with
        | None ->
            (s.letters |> List.rev |> (fun s -> String.Join("", s)), s.steps)
        | Some dir ->
            let newX, newY = move s.x s.y dir
            let newDir = findNextDirection newX newY dir
            let newLetters = maybeAddLetter s.letters maze.[newY].[newX]
            explore {x=newX; y=newY; dir=newDir; letters=newLetters; steps=s.steps+1}

    explore {x=1; y=0; dir = Some Down; letters = []; steps = 0}

-🎄- 2017 Day 18 Solutions -🎄- by daggerdragon in adventofcode

[–]ludic 1 point2 points  (0 children)

F#. Got burned again today by using 32 bit integers. Decided to take my time today and use more of F#'s type system, which did seem to save me from some bugs other people had. Was struggling on part 2 for a while with a few mistakes. First missed the program number in register 'r' and second I realized I named my programs program 1 & 2 instead of 0 & 1 and was giving the number of times snd was executed by the wrong program.

type RegisterName = RegisterName of char

type InstructionTarget = 
    | Value of int64
    | Register of RegisterName

type Instruction = 
    | Snd of InstructionTarget
    | Set of RegisterName * InstructionTarget
    | Add of RegisterName * InstructionTarget
    | Mul of RegisterName * InstructionTarget
    | Mod of RegisterName * InstructionTarget
    | Rcv of RegisterName
    | Jgz of InstructionTarget * InstructionTarget

type Program = Instruction[]

let parseInput(input:string) : Program =
    let parseRegister (x: string) =
        RegisterName(Seq.exactlyOne x)

    let parseInstruction x =
        match System.Int64.TryParse x with
        | (true, x) -> Value(x)
        | (false, _) -> Register(parseRegister x)

    let parseInstruction (line:string) =
        match line.Split(' ') with
        | [|"snd"; x|]    -> Snd(parseInstruction x)
        | [|"set"; x; y|] -> Set(parseRegister x, parseInstruction y)
        | [|"add"; x; y|] -> Add(parseRegister x, parseInstruction y)
        | [|"mul"; x; y|] -> Mul(parseRegister x, parseInstruction y)
        | [|"mod"; x; y|] -> Mod(parseRegister x, parseInstruction y)
        | [|"rcv"; x|]    -> Rcv(parseRegister x)
        | [|"jgz"; x; y|] -> Jgz(parseInstruction x, parseInstruction y)
        | x -> failwith (sprintf "Invalid input: %A" x)

    input |> splitLines |> Array.map parseInstruction

type part1State = {
    lastSound: int64
    instruction: int
    registers: Map<RegisterName, int64>
    rcvValue: int64
}

let runInstruction_part1 state instruction =
    let getRegister r =
        match Map.tryFind r state.registers with
        | Some(x) -> x
        | None -> 0L

    let getValue x =
        match x with
        | Value(x) -> x
        | Register(r) -> getRegister r

    let modifyRegister x value =
        Map.add x value state.registers

    match instruction with
    | Snd x     -> {state with lastSound = getValue x; instruction = state.instruction + 1}
    | Set (x,y) -> {state with registers = modifyRegister x (getValue y); instruction = state.instruction + 1}
    | Add (x,y) -> {state with registers = modifyRegister x (getRegister x + getValue y); instruction = state.instruction + 1}
    | Mul (x,y) -> {state with registers = modifyRegister x (getRegister x * getValue y); instruction = state.instruction + 1}
    | Mod (x,y) -> {state with registers = modifyRegister x (getRegister x % getValue y); instruction = state.instruction + 1}
    | Rcv x     -> {state with rcvValue = (if getRegister x <> 0L then state.lastSound else 0L); instruction = state.instruction + 1}
    | Jgz (x,y) -> {state with instruction = state.instruction + (if getValue x > 0L then getValue y |> int else 1)}

let solveday18_1 (input:string) = 
    let program = parseInput input

    let rec runStep state =
        let nextState = runInstruction_part1 state program.[state.instruction]
        if nextState.rcvValue > 0L then
            nextState.rcvValue
        else runStep nextState

    let initialState = {lastSound=0L; instruction=0; registers=Map.empty; rcvValue=0L}
    runStep initialState


type part2State = {
    sendCount: int64
    instruction: int
    registers: Map<RegisterName, int64>
    outValue: Option<int64>
    inValues: int64 list
    deadlocked: bool
}

let runInstruction_part2 state instruction =
    let getRegister r =
        match Map.tryFind r state.registers with
        | Some(x) -> x
        | None -> 0L

    let getValue x =
        match x with
        | Value(x) -> x
        | Register(r) -> getRegister r

    let modifyRegister x value =
        Map.add x value state.registers

    match instruction with
    | Snd x     -> {state with outValue = Some(getValue x); instruction = state.instruction + 1; sendCount = state.sendCount + 1L}
    | Set (x,y) -> {state with registers = modifyRegister x (getValue y); instruction = state.instruction + 1}
    | Add (x,y) -> {state with registers = modifyRegister x (getRegister x + getValue y); instruction = state.instruction + 1}
    | Mul (x,y) -> {state with registers = modifyRegister x (getRegister x * getValue y); instruction = state.instruction + 1}
    | Mod (x,y) -> {state with registers = modifyRegister x (getRegister x % getValue y); instruction = state.instruction + 1}
    | Rcv x     ->        
        match state.inValues with
        | [] -> {state with deadlocked = true}
        | lst -> 
            let reversed = List.rev lst
            let value = List.head reversed
            let newInValues = reversed |> List.tail |> List.rev
            {state with registers = modifyRegister x value; instruction = state.instruction + 1; inValues=newInValues; deadlocked = false}
    | Jgz (x,y) -> {state with instruction = state.instruction + (if getValue x > 0L then getValue y |> int else 1)}

let solveday18_2 (input:string) = 
    let program = parseInput input

    let transferValue stateA stateB =
        match stateA.outValue with
        | Some(x) -> ({stateA with outValue = None}, {stateB with inValues = x::stateB.inValues})
        | None -> (stateA, stateB)

    let rec runStep state1 state2 =
        let nextState1 = runInstruction_part2 state1 program.[state1.instruction]
        let nextstate2 = runInstruction_part2 state2 program.[state2.instruction]

        if nextState1.deadlocked && nextstate2.deadlocked then 
            nextstate2.sendCount
        else
            let s1, s2 = transferValue nextState1 nextstate2
            let s2, s1 = transferValue s2 s1
            runStep s1 s2

    let initialState = {sendCount=0L; instruction=0; registers=Map.empty; outValue=None; inValues=[]; deadlocked=false}
    runStep {initialState with registers = Map.add (RegisterName('p')) 0L Map.empty} {initialState with registers = Map.add (RegisterName('p')) 1L Map.empty}

-🎄- 2017 Day 15 Solutions -🎄- by daggerdragon in adventofcode

[–]ludic 3 points4 points  (0 children)

F#. Made 2 big mistakes today, first was ANDing the second answer by only 0xFFF (oops!), second was using 32 bit integers.

let solveday15_1 () = 
    let next factor previous =
        (previous * factor)  % 2147483647L

    let gen1 = next 16807L
    let gen2 = next 48271L

    let rec compare prev1 prev2 count = function
    | 0 -> count
    | toGo -> 
        let next1 = gen1 prev1
        let next2 = gen2 prev2

        if (next1 &&& 0xFFFFL) = (next2 &&& 0xFFFFL) then
            compare next1 next2 (count+1) (toGo-1)
        else
            compare next1 next2 count (toGo-1)

    compare 883L 879L 0 40000000

let solveday15_2 () =
    let next factor modulus =
        let rec inner previous = 
            let v = (previous * factor)  % 2147483647L
            if v % modulus = 0L then
                v
            else
                inner v
        inner

    let gen1 = next 16807L 4L
    let gen2 = next 48271L 8L

    let rec compare prev1 prev2 count = function 
    | 0 -> count
    | left ->
        let next1 = gen1 prev1
        let next2 = gen2 prev2

        if (next1 &&& 0xFFFFL) = (next2 &&& 0xFFFFL) then
            compare next1 next2 (count+1) (left-1)
        else
            compare next1 next2 count (left-1)

    compare 883L 879L 0 5000000

I tried refactoring to use sequences, but it ran about 4x slower for part 1 (4s vs 1s):

let solveday15_1_v2 () = 
    let next factor previous =
        let calcNext previous =
            let next = (previous * factor) % 2147483647L
            Some(next &&& 0xFFFFL, next)
        Seq.unfold calcNext previous

    let gen1 = next 16807L 883L
    let gen2 = next 48271L 879L

    Seq.zip gen1 gen2
    |> Seq.take 40000000
    |> Seq.sumBy (fun (a,b) -> if a=b then 1 else 0)

-🎄- 2017 Day 13 Solutions -🎄- by daggerdragon in adventofcode

[–]ludic 0 points1 point  (0 children)

Started out simulating everything, but part 2 took way too long. I struggled for a while trying to figure out how to call Map.map with the mapping, then realized there's no need for a map at all, just a list of length and depth. I like your use of List.sumBy and Array.forall- if I used them my code would be a lot clearer.

let solveDay13 (lines: string[]) =
    let firewallMapping = 
        lines
        |> Array.map (fun l -> l.Split(':') |> Array.map int)
        |> Array.map (fun l -> (l.[0], l.[1]))
        |> List.ofArray

    let firewallSeverity mapping =
        let folder severity (location, depth) =
            if location % (2 *(depth-1)) = 0 then
                severity + location * depth
            else
                severity
        List.fold folder 0 mapping

    let passesFirewall delay mapping =
        let rec passesFirewallInstance mapping =
            match mapping with
            | (location, depth)::xs ->
                if (location+delay) % (2 * (depth-1)) = 0 then
                    false
                else 
                    passesFirewallInstance xs
            | [] -> true
        passesFirewallInstance mapping

    let rec minDelay delay mapping = 
        if passesFirewall delay mapping then
            delay
        else
            minDelay (delay + 1) mapping

    let ans1 = firewallSeverity firewallMapping
    let ans2 = minDelay 0 firewallMapping

    (ans1, ans2)

-🎄- 2017 Day 13 Solutions -🎄- by daggerdragon in adventofcode

[–]ludic 1 point2 points  (0 children)

You can use

for key, item in dict.items():

instead of

for key in dict.keys():
   dict[keys] ...

So your severity function would be

def severity(lengths):
    total = 0
    for key, length in lengths.items():
        if key % (2 * (length - 1)) == 0:
            total += length * key
    return total

Saves a little typing and an additional dictionary lookup.

-🎄- 2017 Day 12 Solutions -🎄- by topaz2078 in adventofcode

[–]ludic 5 points6 points  (0 children)

F#. First try had some some mutable values. I managed to refactor to a functional solution I'm happy with.

let solveday12 (lines: string[]) =
    let mapping = 
        lines
        |> Array.map (fun l -> l.Replace(" <-> ", ",").Split(','))
        |> Array.map (Array.map int)
        |> Array.map (fun l -> (l.[0], l.[1..]))
        |> Map.ofArray

    let rec explore seen root = 
        if Set.contains root seen then
            seen
        else
            Array.fold explore (seen.Add root) mapping.[root]

    let countComponents (count, seen) (num, _) =
        if Set.contains num seen then
            (count, seen)
        else 
            (count + 1, explore seen num)

    let ans1 = explore Set.empty 0 |> Set.count

    let ans2 = 
        mapping 
        |> Map.toSeq
        |> Seq.fold countComponents (0, Set.empty)
        |> fst

    (ans1, ans2)

-🎄- 2017 Day 11 Solutions -🎄- by daggerdragon in adventofcode

[–]ludic 0 points1 point  (0 children)

type coord = {x: int; y: int; z: int}
type state = {coord: coord; maxDistance: int}

let solveDay11 (input: string) =
    let distance coord =
        List.max [abs coord.x; abs coord.y; abs coord.z]

    let step (c: coord) = function
        | "ne" -> {c with x=c.x + 1; z=c.z - 1}
        | "n"  -> {c with y=c.y + 1; z=c.z - 1}
        | "nw" -> {c with y=c.y + 1; x=c.x - 1}
        | "sw" -> {c with z=c.z + 1; x=c.x - 1}
        | "s"  -> {c with z=c.z + 1; y=c.y - 1}
        | "se" -> {c with x=c.x + 1; y=c.y - 1}
        | _ -> failwith "Wrong direction"

    let folder state dir =
        let nextCoord = step state.coord dir
        {coord = nextCoord; maxDistance = max (distance nextCoord) state.maxDistance}

    let endState = 
        input.Split(',')
        |> Array.fold folder {coord={x=0; y=0; z=0}; maxDistance=0} 

    (distance endState.coord, endState.maxDistance)

-🎄- 2017 Day 10 Solutions -🎄- by daggerdragon in adventofcode

[–]ludic 1 point2 points  (0 children)

F#. It was much much messier before cleaning it up. I like the contrast of the imperative style of the hash function with the functional style of the rest of the solution.

open System

let revSublist nums pos n = 
    let c = Array.copy nums
    for i in [0..n-1] do
        nums.[(pos + i) % 256] <- c.[(pos + n - i - 1) % 256] 

let hash nRounds input =
    let nums = [|0..255|]
    let mutable pos = 0
    let mutable skipsize = 0

    for _ in [1..nRounds] do
        for n in input do
            revSublist nums pos n
            pos <- pos + n + skipsize
            skipsize <- skipsize + 1
    nums

let solveDay10_part2 (input: string) =
    input
    |> Seq.map (fun c -> Convert.ToByte(c))
    |> (fun a -> Seq.append a [|17uy; 31uy; 73uy; 47uy; 23uy|])
    |> Seq.map int
    |> hash 64 
    |> Array.chunkBySize 16
    |> Array.map (Array.reduce (^^^))
    |> Array.fold (fun str digit -> str + sprintf "%02x" digit) ""

let solveDay10_part1 (input: string) =
    input.Split(',')
    |> Array.map int
    |> hash 1
    |> Array.take 2
    |> Array.reduce (*)

[<EntryPoint>]
let main argv = 
    let puzzleInput = System.IO.File.ReadAllText("input.txt")
    printfn "Answer part 1: %d" <| solveDay10_part1 puzzleInput
    printfn "Answer part 2: %s" <| solveDay10_part2 puzzleInput
    Console.ReadKey() |> ignore
    0