หน้าเว็บ

วันศุกร์ที่ 15 มีนาคม พ.ศ. 2556

design code CPU scheduling algorithm javascript

กำลังเขียน...
        CPU scheduling algorithm คือ algorithm ที่เอามาใช้สำหรับจัดการ process ที่จะเข้าไปประมวณผลใน CPU ว่าจะให้ process ไหนเข้าไปประมวลผลก่อนหลัง  อาจดูจากลำดับความสำคัญของ process นั้นๆ  หรือเวลาที่ process นั้นๆจะใช้  แล้วก็เป็นตัวกำหนดว่า process นั้นๆสามารถอยู่ใน CPU ได้เป็นเวลานานเท่าใด  ซึ่งมันก็มีได้หลาย algorithm แล้วแต่คนเราจะคิดออก  ในที่นี้จะยกตัวอย่างของ 4 algorithm คือ FirstComeFirstServed, ShortestJobFirst, Priority และ RoundRobin ซึ่งในแต่ละ algorithm มีความหมายดังต่อไปนี้

        FirstComeFirstServed คือ algorithm ที่ใช้สำหรับการจัดการ process โดย ถ้า process ไหนเข้ามาใน process queue ก่อน algorithm นี้ก็จะจัดการนำ process นั้นเข้าไปประมวลผลใน CPU ก่อน  หรือก็คือ  ใครมาก่อนก็ได้ก่อน

        ShortestJobFirst คือ algorithm ที่ใช้สำหรับการจัดการ process โดย ถ้ามี process หนึ่งเข้ามาใน process queue  มันจะทำการจัดเรียงลำดับของ process queue ใหม่ทั้งหมด  เพื่อให้ process ที่ใช้เวลาในการทำงานน้อยที่สุด  ได้เข้าไปประมวลผลใน CPU ก่อน

        Priority คือ algorithm ที่ใช้สำหรับการจัดการ process โดย ถ้ามี process หนึ่งเข้ามาใน process queue  มันจะทำการจัดเรียงลำดับของ process queue ใหม่ทั้งหมด  เพื่อให้ process ที่มีค่าลำดับความสำคัญสูงสุดในขณะนั้น  ได้เข้าไปประมวลผลใน CPU ก่อน

        RoundRobin คือ algorithm ที่ใช้สำหรับการจัดการ process โดยเปิดโอกาสให้แต่ละ process ที่เข้ามาใน   process queue สามารถเข้าไปประมวลผลใน CPU ที่เวลาเท่าๆ กัน เช่น  สมมติว่าผมตั้งเวลาสูงสุด(time slice หรือ quantum time)ที่ process นั้นจะสามารถใช้งาน  CPU ได้   ไว้ที่ 500 ms   ถ้ามี process ที่มี run time(เวลาที่ process ต้องใช้ CPU) เป็น 900 ms  ในรอบแรก process นี้จะสามารถใช้งาน CPU ได้แค่ 500 ms เท่านั้น ส่วนที่เหลืออีก 400 ms จะถูกนำไปต่อท้ายที่ process queue เพื่อเปิดโอกาสให้ process อื่นสามารถเข้ามาใช้งาน CPU ต่อไปได้

ทฤษฎีผ่านไปแล้ว  ทีนี้มาถึงเรื่องของ coding ครับ

        ที่จริงแล้ว  เราจะเขียนด้วยภาษาอะไรก็ได้  เพียงแต่ช่วงนี้ผมกำลังฝึกปรือฝีมือด้านภาษา javascript อยู่  ก็เลยเป็นโอกาสที่ดี  ที่ได้นำความรู้จากตอนที่เรียนมา  มาใช้ในการเขียน code ทดสอบ ครับ

        pattern javascript ที่ใช้ในตัวอย่างนี้จะเป็นเรื่องของ javascript OOP  เป็นหลัก  (ในตัวอย่างนี้อาจจะไม่มีเรื่อง inheritance) บางคนอาจจะดูแปลกตา  หรือไม่เข้าใจโครงสร้างของ code  ก็ลองไปหาอ่าน หาศึกษาเพิ่มเติมกันได้ครับ



        ในตัวอย่างนี้ ผมพยายาม design  code เพื่อลด dependency ต่อกัน หรือจะพูดภาษาชาวบ้านก็คือ ทำให้ code มันอิสระต่อกันให้มากที่สุด  หรือไม่ผูกติดกันแบบแนบแน่นจนเกินไป จนไม่สามารถดึง code บางส่วนออกไปได้นั่นเองครับ   ข้อดีของการเขียนแบบนี้  ก็คือเรื่องของการ maintenance และเรื่องของ scaling เป็นหลัก  คือเราจะถอดอะไร  เพิ่มอะไร  เปลี่ยนอะไร  มันก็จะง่ายขึ้น  และไม่ส่งผลกระทบไปยังส่วนที่เหลือที่ไม่เกี่ยวข้องกัน  เช่น ผมต้องการเปลี่ยน GUI  มันก็ไม่ควรที่จะต้องไปแก้ algorithm ใช่มั้ยครับ  หรือผมต้องการเพิ่ม ลด algorithm ผมก็ไม่จำเป็นต้องไปเขียน GUI ใหม่  หรือ เขียน GUI ให้มันผูกติดกันกับ algorithm นั้นๆ  เพราะถ้าทำแบบนี้  จะเปลี่ยนอะไรที  โคตรลำบากเลยครับ  หรือจะเพิ่ม algorithm ใหม่ที ต้องไปเขียน if else เพื่อเช็คว่า algorithm นี้ต้องใช้  method นี้ทำงานน่ะ algorithm นี้ใช้  method นี้น่ะ ...  อะไรประมาณนี้  คือโลกนี้มีแต่ if else  เพิ่มใหม่  ก็มีการ if ไปเรื่อยๆ

     

Design : 


        ในที่นี้  ผมจะแบ่ง code ออกเป็น layer ต่างๆน่ะครับ  ซึ่งก็จะมี algorithm layer, worker layer และ controller layer
  • algorithm layer  หรือ layer ที่เก็บ algorithm ทั้งหมดที่ implement มาจาก interface CPUScheduler  (ผมเชื่อว่ามีบางคนไม่รู้  ไม่เข้าใจว่า interface คืออะไร  เอาไว้ทำอะไร  อันนี้น่าจะไปหาอ่านเอาเองได้น่ะครับ  ผมไม่อยากเขียน  เพราะว่ามันเป็นเรื่องของ basic  คนเขียนกันเยอะแล้ว  ไม่อยากเขียนซ้ำซ้อนครับ  เรื่อง interface javascript สามารถอ่านได้ที่ การสร้าง interface javascript ) ซึ่ง algorithm layer นี้ ผม design ให้เป็น singleton (class ที่มี instance เดียว) ถามว่าทำไมต้องเป็น singleton  นั่นก็เพราะว่า  algorithm  นี้มีอันเดียว   ควรที่จะเป็นอันเดียว  เมื่อคำนวณเสร็จ  ก็ส่งผลลัพธ์นั้นกลับมา  ไม่มีการเก็บ  state อะไรไว้  และไม่มีตัวอื่นเรียกใช้อีก  นอกจาก worker ตัวเดียวเท่านั้น 

  • worker layer  อะไรคือ worker?   worker  ในที่นี้ผม design เป็น class ปกติ (ไม่ใช่ singleton  คืออนุญาตให้สร้าง instance จาก class นี้เท่าไหร่ก็ได้)  หน้าที่ของ worker คือ คอยจัดการ control algorithm  เช่น การ start, stop, add process, clear process... และเก็บ state ต่างๆ ของ algorithm ใว้  (algorithm แค่คำนวณ  ไม่มีหน้าที่อื่น) ซึ่งแต่ละ algorithm ก็จะมี worker เป็นของตัวเอง  ของใครของมัน 

  • controller layer  เอาไว้สั่งการ worker ต่างๆ(worker ทุกๆตัว)ให้ทำงานตามที่ต้องการ โดยรับคำสั่งมาจาก การกดปุ่ม html ต่างๆ ในที่นี้เป็น singleton    controller จะผูกติดกับ worker เท่านั้น  จะไม่ไปยุ่งเกี่ยวกับ algorithm เพราะถ้าเรามีการเพิ่ม ลบ algorithm ใหม่ เราก็ต้องไปแก้ที่ controller ด้วย  ซึ่งมันไม่ควรที่จะเป็นแบบนั้น  ในส่วนของ GUI(HtmlGui.js) ผมขอผนวกมันไว้ใน layer นี้เลยก็แล้วกันครับ  เพราะว่า  มันไม่ใช่ core application  เพียงแต่เป็นส่วนที่นำผลลัพธ์ที่ได้จากการคำนวณมาแสดงผลเท่านั้น  แต่ก็จะสังเกตเห็นว่า  GUI ผมไม่ได้ไปผูกติดกับ algorithm เลย  มันถูกเอามาแปะไว้ที่ controller ซึ่งก็หมายความว่า  เราจะเปลี่ยน GUI เป็นอะไรก็ได้  จะแต่ง theme ใหม่ style ใหม่ หรือเป็น Log GUI มันก็จะไม่ส่งผลกระทบไปยัง code ส่วนต่างๆด้วย

      ตัวอย่าง code ผมจะเน้นหนักไปในเรื่องของการ maintenance และ scaling เป็นหลักน่ะครับ  performance อาจ drop ลงไปบ้าง  เนื่องจากว่าต้องมีการตรวจเช็ค อะไรต่างๆ  มากมาย  เพื่อให้ code ทำงานได้อย่างถูกต้อง  และง่ายต่อการ debug  แก้ไข   ที่สำคัญคือมีมาตรฐาน เพื่อเป็นข้อตกลงร่วมกันในทีมผู้พัฒนา ว่า คุณจะต้องเขียน code ไปในทิศทางนี้น่ะ  code ถึงจะทำงานได้  ตัวอย่างเช่น  algorithm ที่จะโยนเข้าไปใน worker นั้นจะต้อง implement มาจาก interface CPUScheduler เท่านั้น  เป็นต้น


        code ตัวแรกคือ index.html อันนี้ไม่มีอะไรครับ  หน้าที่ของมันก็แค่เป็น file หลักที่จะใช้สำหรับการแสดงผล  และรวม file javascript ต่างๆเข้ามา  แล้วก็ปุ่มต่างๆ  ก็จะอยู่ที่ไฟล์นี้

index.html
<html>
    <head>
        <link type="text/css" rel="stylesheet" href="gui/style.css"/>
        <script type="text/javascript" src="jquery.js"></script>
        <script type="text/javascript" src="Utilities.js"></script>
        <script type="text/javascript" src="gui/HtmlGui.js"></script>
        <script type="text/javascript" src="Interface.js"></script>
        <script type="text/javascript" src="Process.js"></script>
        <script type="text/javascript" src="algorithms/CPUScheduler.js"></script>
        <script type="text/javascript" src="algorithms/FirstComeFirstServed.js"></script>
        <script type="text/javascript" src="algorithms/ShortestJobFirst.js"></script>
        <script type="text/javascript" src="algorithms/Priority.js"></script>
        <script type="text/javascript" src="algorithms/RoundRobin.js"></script>
        <script type="text/javascript" src="Worker.js"></script>  
        <script type="text/javascript" src="Controller.js"></script>
        <script type="text/javascript" src="application.js"></script>
    </head>
    <body>
        <span id="processNumber">0</span>
        <button id="add">add process</button>
        <button id="start">start</button>
        <button id="stop">stop</button>
        <button id="clear">clear process</button>
        <button id="averageTime">show average time</button> 
    </body> 
</html>
        ต่อมาคือ application.js(javascript) ที่ใช้สำหรับการ control ปุ่ม html ต่างๆ  การเพิ่ม ลด algorithm ก็จะนำมาไว้ที่ file นี้ครับ สังเกตได้จากบรรทัดที่ 2 ถึงบรรทัดที่ 9

  • บรรทัดที่ 2 - 6 นั้น  จะเป็นการ set algorithm ต่างๆ  ลงไป  โดย เราจะจับ algorithm ต่างๆ  ยัดลงไปใน worker   แล้วให้ controller ไปสั่งการ worker อีกที  จากนั้น worker ก็จะนำ algorithm ที่มีอยู่ในขณะนั้นมาทำการคำนวณ  แล้วก็ส่งผลลัพธ์กลับออกมา  ซึ่งในส่วนนี้เราจะเพิ่ม  ลบ  algorithm  อะไรก็ได้  มันจะไม่ส่งผลกระทบไปยัง code ส่วนอื่นๆ  เช่นอยากได้แค่ 2 algorithm ก็แค่ comment algorithm ตัวที่เหลือ  แค่นั้นเอง  เราเรียกวิธีการที่สามารถใส่อะไรต่างๆ  ลงไปได้อย่างหลากหลาย(worker 1 ตัว  รองรับ algorithm ได้หลายแบบ) นี้ว่า polymorphism (คุณสมบัติหนึ่งของ OOP)

  • บรรทัดที่ 7 - 9 เป็น callback function (ผมเชื่อว่าบางคนไม่รู้จัก callback function และไม่รู้ว่ามันเขียนยังไง  หรือทำงานยังไง  ลองสังเกตจากตัวอย่าง code ดูครับ  แล้วคุณจะเข้าใจ) ที่จะมีการ คืนค่าของ algorithm ต่างๆ  ออกมา  เพื่อนำมาวาดเป็น GUI (ตาราง)  ตรงนี้  เราจะใช้ GUI แบบไหนก็ได้  แล้วแต่เราครับ  ซึ่งมันก็จะไม่ส่งผลกระทบไปยัง code ส่วนอื่นๆเช่นกัน  ถึงจะเอา GUI ออก  code ที่เหลือก็ยังทำงานได้  แค่มันไม่แสดงผลลัพธ์ออกมาแค่นั้นเอง

  • บรรทัดที่ 13 - 17  method run เป็น callback function ที่เอาไว้รับ process ปัจจุบันที่ประมวลผลอยู่ หรือประมวลผลเสร็จแล้ว  มาวาดลง GUI ครับ  

  • บรรทัดที่ 19 -25 method addProcess เป็น callback function ที่เอาไว้รับ process ปัจจุบันที่กำลังถูกเพิ่มเข้าไปยัง process queue

        ตัวอย่าง code ที่เหลือผมขอไม่อธิบายน่ะครับ  ถ้าอ่านจาก code ก็น่าจะเข้าใจ  เพราะผมพยายามใช้ keyword ที่ใกล้เคียงกับความเป็นจริง  เพื่อให้อ่านเข้าใจง่ายแล้ว

application.js
$(document).ready(function() {
    Controller.setWorkers([
        new Worker(FirstComeFirstServed), //set up algorithm
        new Worker(ShortestJobFirst),
        new Worker(Priority),
        new Worker(RoundRobin)
    ], function(object) {
        HtmlGui.drawTable("body", object.algorithm); //draw GUI
    });


    var numberOfProcess = 0;
    Controller.run(function(object) {
        if ("SUCCESS" === object.status) {
            HtmlGui.drawRow(object.algorithm, object.process); //draw GUI
        }
    });

    $("button#add").click(function() {
        Controller.addProcess(function(process) {
            console.log(process);
            numberOfProcess = numberOfProcess + 1;
            $("#processNumber").html(numberOfProcess);
        });
    });

    $("button#start").click(function() {
        $(this).addClass("active");

        Controller.start(function(object) {
            console.log("start.");
            console.log(object);
        });
    });

    $("button#stop").click(function() {
        $("button#start").removeClass("active");

        Controller.stop(function(object) {
            console.log("stop.");
            console.log(object);
        });
    });

    $("button#clear").click(function() {
        $("button#start").removeClass("active");

        Controller.clearProcess(function(object) {
            console.log("clear.");
            console.log(object);
        });
    });

    $("button#averageTime").click(function() {
        Controller.getAverageTime(function(object) {
            var averageTime = "<div class='average-time'>" + object.averageTime + "</div>";
            $("#" + object.algorithm + "_GUI").append(averageTime);
        });
    });
}); 
        Controller.js ทำหน้าที่ control worker ต่างๆ (worker ทุกตัว) ให้ทำงานตามที่ต้องการ เช่น การ start, stop, add process, clear process ... เป็นต้น

  • บรรทัดที่ 9 - 29  เป็นการรับค่า worker เข้ามา  จากนั้นก็ตรวจสอบว่า worker ที่รับเข้ามาเป็น array ของ class Worker หรือไม่  ถ้าไม่ใช่ ก็จะโยน error ออกมา ซึ่ง error นั้นจะไปออกที่ catch(ex)  แต่ถ้าใช่ก็จะส่งกลับออกไปทาง callback function

  • บรรทัดที่ 32 - 44 เป็นสั่งการ start worker แต่ละตัว  จากนั้นก็ส่ง object ที่ประกอบด้วย algorithm และ status เป็น STARTED กลับออกไปทาง callback function

  • บรรทัดที่ 46 - 55 เป็น การส่งต่อ(ส่งออก) callback function จาก worker ไปให้ controller เพื่อรับ process ปัจจุบันที่กำลัง EXECUTE อยู่  หรือ SUCCESS แล้ว

  • บรรทัดที่ 71 - 91 method addProcess ทำหน้าที่ add process เข้าไปยัง worker แต่ละตัว  ทีนี้จะเกิดข้อสังเกตอย่างนึงว่า  ทำไม  ผมไม่สร้าง process ขึ้นมาตัวเดียว  แล้วยัดให้กับ worker ทุกๆ  ตัวแทน  ทำไมผมต้อง  new process ขึ้นมาใหม่เสมอ อันนี้ไม่มีอะไรครับ  คือ เพราะถ้าเรา new process ขึ้นมาตัวเดียว  แล้วยัดให้กับ worker ทุกๆ  worker ทุกตัวก็จะอ้างถึง process ตัวเดียวกัน  ทีนี้ลองนึกภาพดูครับ  ถ้ามันอ้างถึง process ตัวเดียวกันแล้วจะเกิดอะไรขึ้น  มันก็คำนวณผิดน่ะสิครับ  process ตัวนั้น  มันก็จะได้ค่าหลังสุด (ค่าที่ได้จาก worker  ตัวสุดท้ายเท่านั้น)  อย่าลืมว่ามันเป็น reference type  ที่จะชี้ไปที่ object ตัวเดียวกัน  ซึ่งถ้า worker ตัวที่ 1 ทำให้ค่ามันเปลี่ยน  worker ตัวที่ 2 3 4 ก็จะได้รับผลกระทบนั้นๆ  ตามไปด้วย  ฉะนั้นเวลาเขียน code ให้คำนึงถึงเรื่องนี้ด้วยครับ  เพราะ code เราคือสั่งให้ worker มันทำงานพร้อมกันทั้งหมด  มันเลยต้องเป็น process คนละตัวกัน ไม่มี reference ต่อกันครับ  จะได้ไม่เกิดข้อผิดพลาดตามมา

        code ส่วนที่เหลือไม่ขออธิบายครับ  เพราะว่าน่าจะอ่านเข้าใจ  เพราะ keyword ที่ใช้ใน code น่าจะทำให้อ่านเข้าใจได้

Controller.js
var Controller = (function($) {
    var _pid = 0;
    var _workers;

    return {
        setWorkers: function(workers, callback) {
            callback = callback || function() { };

            try {
                if(typeof workers !== 'object' || workers.length === undefined){
                    throw new Error("First argument of Controller.setWorkers([worker1, worker2, ...], callback) is not \"Array of Worker\", but is \"" + (typeof workers) + "\".");
                }

                _workers = workers;

                for (var index in _workers) {
                    var worker = _workers[index];
                    if(worker.constructor !== Worker){
                        throw new Error("Argument worker[" + index + "] of Controller.setWorkers([worker1, worker2, ...], callback) is not \"Worker\", but is \"" + (typeof worker) + "\".");
                    } 

                    callback({
                        algorithm: worker.getAlgorithm(),
                        status: "SET_UP"
                    });
                }
            } catch (ex) {
                alert(ex);
            }
        },

        start: function(callback) {
            callback = callback || function() { };

            for (var index in _workers) {
                var worker = _workers[index];
                worker.start();

                callback({
                    algorithm: worker.getAlgorithm(),
                    status: "STARTED"
                });
            }
        },

        run: function(callback) {
            callback = callback || function() { };

            for (var index in _workers) {
                var worker = _workers[index];
                worker.run(function(object) {
                    callback(object);
                });
            }
        },

        stop: function(callback) {
            callback = callback || function() { };

            for (var index in _workers) {
                var worker = _workers[index];
                worker.stop();

                callback({
                    algorithm: worker.getAlgorithm(),
                    status: "STOPED"
                });
            }
        },

        addProcess: function(callback) {
            callback = callback || function() { };
            _pid = _pid + 1;

            var runTime = (new Number(Math.random() * 1000)).toFixed(3);
            var arrivalTime = (new Date()).getTime();
            var priority = parseInt(Math.random() * 10);

            var process;
            for (var index in _workers) {
                process = new Process("PID" + _pid);
                process.setRunTime(runTime)
                        .setRemainingTime(runTime)
                        .setArrivalTime(arrivalTime)
                        .setPriority(priority);

                _workers[index].addProcess(process);
            }

            callback(process);
        },

        clearProcess: function(callback) {
            callback = callback || function() { };

            for (var index in _workers) {
                var worker = _workers[index];
                worker.clearProcess();

                callback({
                    algorithm: worker.getAlgorithm(),
                    status: "STOPED"
                });
            }
        },

        getAverageTime: function(callback) {
            callback = callback || function() { };

            for (var index in _workers) {
                var worker = _workers[index];

                callback({
                    algorithm: worker.getAlgorithm(),
                    averageTime: worker.getAverageTime()
                });
            }
        }
    };
})(jQuery); 
        ต่อมาคือ Worker.js ทำหน้าที่ในการจัดการ และเก็บ state ต่างๆของแต่ละ  algorithm  ไว้  สามารถ new instance ขึ้นมาใหม่ได้เรื่อยๆ   แต่ละ algorithm จะมี worker เป็นของใครของมัน  ไม่ยุ่งเกียวต่อกัน


  • บรรทัดที่ 7- 8 constructor ของ Worker require algorithm ที่ implement interface CPUScheduler  กล่าวคือ algorithm อะไรก็ตามที่ถูกส่งเข้ามายัง constructor จะต้องมี method เหมือนที่ประกาศไว้ใน  CPUScheduler ครบทุก method นั่นเอง (จะเกินก็ได้ แต่ต้องไม่ขาดตามที่ประกาศไว้)

Worker.js
var Worker = (function() {
    var _runningStep = 10; //millisecond

    /**
     * constructor require CPUScheduler algorithm
     */
    return function(algor) {
        Interface.ensureImplements(algor, [CPUScheduler]);
        //
        var algorithm = algor;
        var processQueue = [];
        var runningState = false;
        var callback = function() {  };
        var beforeComplete = true;
        var sumTurnAroundTime = 0;
        var numberOfProcess = 0;

        setInterval(function() {

            if (runningState) {

                var process = undefined;
                if (beforeComplete) {
                    process = Arrays.getFirst(processQueue);
                }

                if (process !== undefined) {
                    process.setStartTime((new Date()).getTime());
                    beforeComplete = false;

                    callback({
                        algorithm: algorithm.getName(),
                        process: process,
                        status: "EXECUTE"
                    });

                    setTimeout(function() {

                        process.setFinishTime((new Date()).getTime());
                        var turnAroundTime = process.getFinishTime() - process.getArrivalTime();
                        sumTurnAroundTime = sumTurnAroundTime + turnAroundTime;
                        process.setTurnAroundTime(turnAroundTime);

                        callback({
                            algorithm: algorithm.getName(),
                            process: process,
                            status: "SUCCESS"
                        });

                        var indexOf = processQueue.indexOf(process);
                        delete processQueue[indexOf];
                        beforeComplete = true;

                        if (process.getRemainingTime() !== 0) {
                            process.setRunTime(process.getRemainingTime());
                            processQueue.push(process);
                        }

                    }, algorithm.execute(process));
                }

            }
        }, _runningStep);


        this.addProcess = function(process) {
            if (process.constructor !== Process) {
                throw new Error("Argument process of Worker.addProcess(process) is not \"Process\", but is \"" + (typeof process) + "\".");
            }

            processQueue.push(process);
            processQueue = algorithm.sort(processQueue);
            numberOfProcess = numberOfProcess + 1;

            return this;
        };

        this.start = function() {
            runningState = true;

            return this;
        };

        this.stop = function() {
            runningState = false;

            return this;
        };

        this.run = function(cbf) {
            callback = cbf || callback;

            return this;
        };

        this.getAlgorithm = function() {
            return algorithm.getName();
        };

        this.clearProcess = function() {
            this.stop();

            for (var index in processQueue) {
                delete processQueue[index];
            }

            return this;
        };

        this.getAverageTime = function() {
            return (new Number(sumTurnAroundTime / numberOfProcess)).toFixed(3);
        };
    };
})();
Process.js
var Process = (function() {

    return function(pid) {

        var id = pid;
        var arrivalTime;
        var runTime;
        var remainingTime;
        var startTime;
        var finishTime;
        var turnAroundTime;
        var priority;

        //gettter
        this.getId = function() {
            return id;
        };

        this.getArrivalTime = function() {
            return arrivalTime;
        };

        this.getRunTime = function() {
            return runTime;
        };

        this.getRemainingTime = function() {
            return remainingTime;
        };

        this.getStartTime = function() {
            return startTime;
        };

        this.getFinishTime = function() {
            return finishTime;
        };

        this.getTurnAroundTime = function() {
            return turnAroundTime;
        };

        this.getPriority = function() {
            return priority;
        };

        //setter 
        this.setArrivalTime = function(at) {
            arrivalTime = at;
            return this;
        };

        this.setRunTime = function(rt) {
            runTime = rt;
            return this;
        };

        this.setRemainingTime = function(rmt) {
            remainingTime = rmt;
            return this;
        };

        this.setStartTime = function(st) {
            startTime = st;
            return this;
        };

        this.setFinishTime = function(ft) {
            finishTime = ft;
            return this;
        };

        this.setTurnAroundTime = function(tat) {
            turnAroundTime = tat;
            return this;
        };

        this.setPriority = function(prt) {
            priority = prt;
            return this;
        };
    };
})();
Interface.js อ้างอิงจากบทความเรื่อง การสร้าง interface javascript

CPUScheduler.js
/**
 * create standard interface for define CPUScheduler algorithm
 */
var CPUScheduler = new Interface("CPUScheduler", ["getName", "sort", "execute"]);
//
algorithms/FirstComeFirstServed.js
var FirstComeFirstServed = (function() {
    var _name = "FirstComeFirstServed";

    return {
        getName: function() {
            return _name;
        },

        sort: function(processQueue) {
            /* not use */

            return processQueue;
        },

        execute: function(process) {
            process.setRemainingTime(0);

            return process.getRunTime();
        }
    };
})(); 
algorithms/ShortestJobFirst.js
var ShortestJobFirst = (function() {
    var _name = "ShortestJobFirst";

    return {
        getName: function() {
            return _name;
        },

        sort: function(processQueue) {
            processQueue.sort(function(process1, process2) {
                return process1.getRunTime() - process2.getRunTime();
            });

            return processQueue;
        },

        execute: function(process) {
            process.setRemainingTime(0);

            return process.getRunTime();
        }
    };
})(); 
algorithms/Priority.js
var Priority = (function() {
    var _name = "Priority";

    return {
        getName: function() {
            return _name;
        },

        sort: function(processQueue) {
            processQueue.sort(function(process1, process2) {
                return process2.getPriority() - process1.getPriority();
            });

            return processQueue;
        },

        execute: function(process) {
            process.setRemainingTime(0);

            return process.getRunTime();
        }
    };
})(); 
algorithms/RoundRobin.js
var RoundRobin = (function() {
    var _name = "RoundRobin";
    var _timeSlice = 500; //millisecond

    return {
        getName: function() {
            return _name;
        },

        sort: function(processQueue) {
            /* not use */

            return processQueue;
        },

        execute: function(process) {
            var remainingTime = process.getRemainingTime();
            if (remainingTime <= _timeSlice) {
                process.setRemainingTime(0);

                return remainingTime;
            } else {
                remainingTime = remainingTime - _timeSlice;
                process.setRemainingTime((new Number(remainingTime)).toFixed(3));

                return _timeSlice;
            }
        }
    };
})();
gui/HtmlGui.js
var HtmlGui = (function($) {

    return {
        drawTable: function(selector, algorithmName) {
            $(selector).append('<div id="' + algorithmName + '_GUI" class="gui-wrapper"></div>');

            var html = '';
            html = html + '<div class="algorithm">';
                html = html + algorithmName;
                html = html + '<span class="current-process">NOT</span>';
            html = html + '</div>';
            html = html + '<div class="header">';
                html = html + '<span>process</span>';
                html = html + '<span>piority</span>';
                html = html + '<span>arrival time</span>';
                html = html + '<span>run time (ms)</span>';
                html = html + '<span>start time</span>';
                html = html + '<span>finish time</span>';
                html = html + '<span>remaining time (ms)</span>';
                html = html + '<span>turn around time (ms)</span>';
            html = html + '</div>';

            $("#" + algorithmName + "_GUI").append(html);
        },

        drawRow: function(algorithmName, process) {
            var html = "";
            html = html + "<div>";
                html = html + "<span>" + process.getId() + "</span>";
                html = html + "<span>" + process.getPriority() + "</span>";
                html = html + "<span>" + Timer.getTime(process.getArrivalTime()) + "</span>";
                html = html + "<span>" + process.getRunTime() + "</span>";
                html = html + "<span>" + Timer.getTime(process.getStartTime()) + "</span>";
                html = html + "<span>" + Timer.getTime(process.getFinishTime()) + "</span>";
                html = html + "<span>" + process.getRemainingTime() + "</span>";
                html = html + "<span class='ternaround-time'>" + process.getTurnAroundTime() + "</span>";
            html = html + "</div>";

            $("#" + algorithmName + "_GUI").append(html);
            $("#" + algorithmName + "_GUI .algorithm .current-process").html(process.getId());
        }
    };
})(jQuery);

Utilities.js
var Arrays = (function() {

    return {
        getFirst: function(arrayList) {
            for (var index in arrayList) {
                return arrayList[index];
            }

            return undefined;
        }
    };
})();


var Timer = (function() {

    return {
        getTime: function(timestamp) {
            var date = new Date(timestamp);
            return date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + ":" + date.getMilliseconds();
        }
    };
})();
gui/style.css
.gui-wrapper{
    margin-top : 50px;
    background : rgb(243, 243, 243);
    width : 1227px;
    border : solid 1px #ddd;
    border-radius : 3px; 
    -moz-border-radius : 3px;
    -webkit-border-radius : 3px; 
}

.gui-wrapper .header > span{
    background : rgb(221, 221, 221);
    border-radius : 3px;
}

.algorithm{
    padding : 5px;
    font-weight : bold;
    font-size : 12pt;
    position : relative;
}

.header span{
    font-weight : bold; 
}

button{
    background : #f5f5f5;
    border : solid 1px #ddd;
    padding : 5px;
    border-radius : 3px; 
    -moz-border-radius : 3px;
    -webkit-border-radius : 3px;  

    box-shadow : inset 1px 1px 1px #fafafa; 
    -moz-box-shadow : inset 1px 1px 1px #fafafa;
    -webkit-box-shadow : inset 1px 1px 1px #fafafa;  

    font-weight : bold;
}

button:hover{
    cursor : pointer;
}

button.active{
    background : orange;
    color : #fff;
    border-color : rgb(255, 147, 8);

    box-shadow : inset 1px 1px 1px rgb(243, 189, 18); 
    -moz-box-shadow : inset 1px 1px 1px rgb(243, 189, 18);
    -webkit-box-shadow : inset 1px 1px 1px rgb(243, 189, 18);  
}

.algorithm .current-process{
    background : rgb(4, 207, 53);
    color : #fff;
    position : absolute;
    top : 2px;
    right : 2px;
    width : auto;
    padding : 2px 5px;
    border-radius : 3px; 
    -moz-border-radius : 3px;
    -webkit-border-radius : 3px; 
}

.average-time{
    text-align : right;
    padding : 5px;
    background : rgb(179, 179, 179);
    color : #fff;
}

.gui-wrapper span{
    width : 145px;
    padding : 5px;
    display : inline-block;
    background : rgb(255, 255, 255); 
    margin : 2px;
}

.gui-wrapper span:first-child  + span{
    width : 100px;
}

#processNumber{
    padding : 5px;
    background : red;
    color : #fff;
    border-radius : 3px; 
    -moz-border-radius : 3px;
    -webkit-border-radius : 3px; 
}

ไม่มีความคิดเห็น:

แสดงความคิดเห็น