{
    "version": "1.7",
    "metadata": {
        "name": "megabite_latest",
        "created": "2026-04-05T00:31:04.114356755-04:00",
        "modified": "2026-04-05T00:31:04.116659569-04:00"
    },
    "programs": {
        "DUT_Motor": {
            "source": "TYPE Motor_Type :\nSTRUCT\n    Running : BOOL;\n    Speed : REAL;\n    Current : REAL;\n    Fault : BOOL;\n    Hours : DINT;\nEND_STRUCT\nEND_TYPE\n",
            "task": "StructTest",
            "modified": "2026-04-05T00:31:04.114390765-04:00"
        },
        "POU_DogControl": {
            "source": "PROGRAM POU_DogControl\nVAR\n    state : INT := 0;\n    p2_ok : BOOL := FALSE;\n    port : STRING;\n\n    (* Eyes *)\n    eye_x : INT := 64;\n    eye_y : INT := 27;\n    lid : INT := 0;\n\n    (* Servos — duty in microseconds, 1500=center, 600-2400 range *)\n    servo_0 : INT := 4915;\n    servo_2 : INT := 4915;\n    servo_4 : INT := 4915;\n    servo_6 : INT := 4915;\n    servo_47 : INT := 4915;\n    servo_45 : INT := 4915;\n    servo_43 : INT := 4915;\n    servo_41 : INT := 4915;\n\n    (* Servos — speed *)\n    servo_0_spd : INT := -20;\n    servo_2_spd : INT := -20;\n    servo_4_spd : INT := -20;\n    servo_6_spd : INT := -20;\n    servo_47_spd : INT := -20;\n    servo_45_spd : INT := -20;\n    servo_43_spd : INT := -20;\n    servo_41_spd : INT := -20;\n\n    (* Sound *)\n    play_track : INT := 0;\n    last_track : INT := 0;\n\n    (* Idle behavior — scan counters (~192ms/scan) *)\n    look_cnt : INT := 0;\n    look_target : INT := 15;\n    blink_cnt : INT := 0;\n    blink_target : INT := 25;\n    blink_hold_cnt : INT := 0;\n    blinking : BOOL := FALSE;\n    ear_cnt : INT := 0;\n    ear_target : INT := 30;\n    ear_hold_cnt : INT := 0;\n    ear_twitching : BOOL := FALSE;\n\n    (* Ear neutral positions *)\n    ear_0_neutral : INT := 4915;\n    ear_47_neutral : INT := 4915;\nEND_VAR\n\nCASE state OF\n    0: (* INIT *)\n        port := SERIAL_FIND('Parallax');\n        IF LEN(port) > 0 THEN\n            p2_ok := P2_INIT('p2', port, '/home/james/projects/GOPLC/go-p2/firmware/cyclic_io.binary');\n            IF p2_ok THEN\n                (* Init eyes *)\n                P2_CMD('p2', 'eye_start', 'sda', 11, 'scl', 10, 'addr', 60);\n                P2_CMD('p2', 'eye_start', 'sda', 25, 'scl', 24, 'addr', 60);\n\n                (* Init servos — 50Hz for standard servos *)\n                P2_CMD('p2', 'pwm_setup', 'pin', 0, 'freq', 50);\n                P2_CMD('p2', 'pwm_setup', 'pin', 2, 'freq', 50);\n                P2_CMD('p2', 'pwm_setup', 'pin', 4, 'freq', 50);\n                P2_CMD('p2', 'pwm_setup', 'pin', 6, 'freq', 50);\n                P2_CMD('p2', 'pwm_setup', 'pin', 47, 'freq', 50);\n                P2_CMD('p2', 'pwm_setup', 'pin', 45, 'freq', 50);\n                P2_CMD('p2', 'pwm_setup', 'pin', 43, 'freq', 50);\n                P2_CMD('p2', 'pwm_setup', 'pin', 41, 'freq', 50);\n\n                (* Init DF Mini UART — 9600 baud *)\n                P2_CMD('p2', 'uart_setup', 'ch', 0, 'tx_pin', 30, 'rx_pin', 29, 'baud', 9600);\n\n                (* Save ear neutral positions *)\n                ear_0_neutral := servo_0;\n                ear_47_neutral := servo_47;\n\n                state := 1;\n            END_IF;\n        END_IF;\n\n    1: (* MAIN LOOP — update hardware + idle behaviors *)\n\n        (* === IDLE BEHAVIORS === *)\n\n        (* Eye look-around: every ~3s *)\n        look_cnt := look_cnt + 1;\n        IF look_cnt >= look_target THEN\n            eye_x := RANDOM_RANGE(30, 98);\n            eye_y := RANDOM_RANGE(15, 40);\n            look_cnt := 0;\n            look_target := RANDOM_RANGE(10, 25);\n        END_IF;\n\n        (* Eye blink: every ~5s *)\n        IF NOT blinking THEN\n            blink_cnt := blink_cnt + 1;\n            IF blink_cnt >= blink_target THEN\n                blinking := TRUE;\n                lid := 100;\n                blink_cnt := 0;\n                blink_hold_cnt := 0;\n            END_IF;\n        ELSE\n            blink_hold_cnt := blink_hold_cnt + 1;\n            IF blink_hold_cnt >= 1 THEN\n                lid := 0;\n                blinking := FALSE;\n                blink_target := RANDOM_RANGE(15, 35);\n            END_IF;\n        END_IF;\n\n        (* Ear twitch: every ~6s *)\n        IF NOT ear_twitching THEN\n            ear_cnt := ear_cnt + 1;\n            IF ear_cnt >= ear_target THEN\n                ear_twitching := TRUE;\n                servo_0 := ear_0_neutral + RANDOM_RANGE(-400, 400);\n                servo_47 := ear_47_neutral + RANDOM_RANGE(-400, 400);\n                ear_cnt := 0;\n                ear_hold_cnt := 0;\n            END_IF;\n        ELSE\n            ear_hold_cnt := ear_hold_cnt + 1;\n            IF ear_hold_cnt >= 3 THEN\n                servo_0 := ear_0_neutral;\n                servo_47 := ear_47_neutral;\n                ear_twitching := FALSE;\n                ear_target := RANDOM_RANGE(25, 50);\n            END_IF;\n        END_IF;\n\n        (* === HARDWARE OUTPUT === *)\n\n        (* Eyes — update every scan from variables *)\n        P2_CMD('p2', 'eye_move', 'eye', 0, 'x', eye_x, 'y', eye_y);\n        P2_CMD('p2', 'eye_move', 'eye', 1, 'x', eye_x, 'y', eye_y);\n        P2_CMD('p2', 'eye_lid', 'eye', 0, 'position', lid);\n        P2_CMD('p2', 'eye_lid', 'eye', 1, 'position', lid);\n\n        (* Servos — update from variables *)\n        P2_CMD('p2', 'servo_move', 'pin', 0, 'duty', servo_0, 'speed', servo_0_spd);\n        P2_CMD('p2', 'servo_move', 'pin', 2, 'duty', servo_2, 'speed', servo_2_spd);\n        P2_CMD('p2', 'servo_move', 'pin', 4, 'duty', servo_4, 'speed', servo_4_spd);\n        P2_CMD('p2', 'servo_move', 'pin', 6, 'duty', servo_6, 'speed', servo_6_spd);\n        P2_CMD('p2', 'servo_move', 'pin', 47, 'duty', servo_47, 'speed', servo_47_spd);\n        P2_CMD('p2', 'servo_move', 'pin', 45, 'duty', servo_45, 'speed', servo_45_spd);\n        P2_CMD('p2', 'servo_move', 'pin', 43, 'duty', servo_43, 'speed', servo_43_spd);\n        P2_CMD('p2', 'servo_move', 'pin', 41, 'duty', servo_41, 'speed', servo_41_spd);\n\n        (* Sound — play track when changed *)\n        IF play_track <> last_track AND play_track > 0 THEN\n            P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF060600001EEF');\n            CASE play_track OF\n                1: P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF0603000001EF');\n                2: P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF0603000002EF');\n                3: P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF0603000003EF');\n                4: P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF0603000004EF');\n                5: P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF0603000005EF');\n                6: P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF0603000006EF');\n                7: P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF0603000007EF');\n                8: P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF0603000008EF');\n                9: P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF0603000009EF');\n                10: P2_CMD('p2', 'uart_tx', 'ch', 0, 'data', '7EFF060300000AEF');\n            END_CASE;\n            last_track := play_track;\n        END_IF;\n\n    ELSE\n        state := 0;\nEND_CASE;\nEND_PROGRAM",
            "task": "Main",
            "modified": "2026-04-05T00:31:04.114392796-04:00"
        },
        "POU_First": {
            "source": "PROGRAM POU_First\nVAR\n    a : INT := 1;\nEND_VAR\n    a := a + 1;\nEND_PROGRAM",
            "task": "Main",
            "modified": "2026-04-05T00:31:04.114395088-04:00"
        },
        "POU_Second": {
            "source": "PROGRAM POU_Second\nVAR\n    b : INT := 100;\nEND_VAR\n    b := b + 1;\nEND_PROGRAM",
            "task": "Main",
            "modified": "2026-04-05T00:31:04.114376963-04:00"
        },
        "POU_StructTest": {
            "source": "PROGRAM POU_StructTest\nVAR\n    pump : Motor_Type;\n    fan : Motor_Type;\n    scan_count : DINT;\n    test777: DINT:=723;\nEND_VAR\n\nscan_count := scan_count + 1;\n\n(* Simulate pump running *)\npump.Running := TRUE;\npump.Speed := 1750.0;\npump.Current := 12.5;\npump.Fault := FALSE;\npump.Hours := pump.Hours + 1;\n\n(* Simulate fan with varying speed *)\nfan.Running := TRUE;\nfan.Speed := 3450.0;\nfan.Current := 5.2;\nfan.Fault := FALSE;\nfan.Hours := fan.Hours + 1;\n\nEND_PROGRAM\n",
            "task": "StructTest",
            "modified": "2026-04-05T00:31:04.114388369-04:00"
        }
    },
    "config_yaml": "api_port: 8302\nauto_start: true\ntasks:\n    - name: Main\n      programs:\n        - POU_Second\n        - POU_DogControl\n        - POU_First\n      scan_time_ms: 100\n      type: periodic\n      watchdog_ms: 200\n    - name: StructTest\n      priority: 10\n      programs:\n        - POU_StructTest\n        - DUT_Motor\n      scan_time_ms: 100\n      type: periodic\n      watchdog_ms: 200\n",
    "tasks": [
        {
            "name": "Main",
            "type": "periodic",
            "priority": 0,
            "scan_time_ms": 100,
            "scan_time_us": 0,
            "programs": [
                "POU_Second",
                "POU_DogControl",
                "POU_First"
            ],
            "watchdog_ms": 200,
            "watchdog_fault": false,
            "watchdog_halt": false,
            "cpu_affinity": 0
        },
        {
            "name": "StructTest",
            "type": "periodic",
            "priority": 10,
            "scan_time_ms": 100,
            "scan_time_us": 0,
            "programs": [
                "POU_StructTest",
                "DUT_Motor"
            ],
            "watchdog_ms": 200,
            "watchdog_fault": false,
            "watchdog_halt": false,
            "cpu_affinity": 0
        }
    ],
    "io_mapping": {
        "DiscreteInputs": null,
        "Coils": null,
        "InputRegisters": null,
        "HoldingRegisters": null,
        "MemoryWords": null
    },
    "protocols": {
        "Modbus": {
            "Enabled": false,
            "Port": 0
        },
        "ModbusMaster": {
            "Enabled": false,
            "Host": "",
            "Port": 0,
            "PollRate": 0
        },
        "ModbusServers": null,
        "ModbusClients": null,
        "S7": {
            "Enabled": false,
            "Port": 0,
            "IORegisters": 0
        },
        "S7Client": {
            "Enabled": false,
            "Host": "",
            "Port": 0,
            "Rack": 0,
            "Slot": 0,
            "PollRate": 0,
            "TimeoutMs": 0
        },
        "FINS": {
            "Enabled": false,
            "Host": "",
            "Port": 0,
            "DestNode": 0,
            "SrcNode": 0,
            "PollRate": 0
        },
        "ENIP": {
            "Enabled": false,
            "Port": 0,
            "Tags": null
        },
        "ENIPScanner": {
            "Enabled": false,
            "Host": "",
            "Port": 0,
            "Slot": 0,
            "PollRate": 0,
            "Tags": null
        },
        "DNP3": {
            "Enabled": false,
            "Port": 0,
            "IORegisters": 0
        },
        "DNP3Master": {
            "Enabled": false,
            "Host": "",
            "Port": 0,
            "LocalAddress": 0,
            "RemoteAddress": 0,
            "PollRate": 0
        },
        "OPCUA": {
            "Enabled": false,
            "Port": 0,
            "Endpoint": "",
            "IORegisters": 0
        },
        "OPCUAClient": {
            "Enabled": false,
            "Endpoint": "",
            "Policy": "",
            "Mode": "",
            "PollRate": 0
        },
        "IEC104": {
            "Enabled": false,
            "Port": 0,
            "CommonAddr": 0,
            "IORegisters": 0
        },
        "IEC104Client": {
            "Enabled": false,
            "Host": "",
            "Port": 0,
            "CommonAddr": 0
        },
        "BACnet": {
            "Enabled": false,
            "Port": 0,
            "DeviceID": 0,
            "DeviceName": "",
            "IORegisters": 0
        },
        "BACnetClient": {
            "Enabled": false,
            "Host": "",
            "Port": 0,
            "PollRate": 0
        },
        "SNMPAgent": {
            "Enabled": false,
            "Port": 0,
            "Community": ""
        },
        "SNMPClient": {
            "Enabled": false,
            "Host": "",
            "Port": 0,
            "Community": "",
            "Version": "",
            "PollRate": 0
        },
        "MQTT": {
            "Enabled": false,
            "Port": 0,
            "WSPort": 0
        },
        "MQTTClient": {
            "Enabled": false,
            "Broker": "",
            "ClientID": "",
            "Username": "",
            "Password": ""
        },
        "FINSServer": {
            "Enabled": false,
            "Port": 0,
            "Node": 0
        }
    }
}