文章作者:環球物流諮詢首席諮詢顧問——黃堯笛
記錄下物流系統執行的資料是物流模擬系統最為重要的環節。我們在構建模擬系統時要全面、充分的考慮到系統執行中需要輸出哪些資料,並在MATLAB中建立資料表,將系統執行的資料記錄到表裡,以供下一環節的資料統計與分析呼叫。本文將簡要介紹筆者在構建物流模擬系統時的資料記錄方式。
資料表產生的邏輯
1、流程活動
“卸貨”、“檢驗”、“理貨”、“上架”、“分揀”等流程為主要倉儲流程活動(二級流程可根據實際需求在流程庫裡匯出)。流程中的活動規則決定了模擬系統中服務檯的執行機制。
倉儲運作參考流程(示例)
2、資源呼叫
流程活動中需要呼叫的“人、設施、裝置”資源,資源的狀態分為“佔用、空閒”兩種狀態,如果資源被佔用,則需要等待,反之執行。
邏輯舉例
3、資料記錄
各個服務檯在進行活動時需要記錄活動資料,活動資料的結構如前文中所說,可根據系統輸出的需要進行構造,本文中舉例說明資料記錄中的幾個重要的資料輸出。活動時間記錄時分為:開始時間與結束時間,上一活動結束到下一活動開始的時間空閒為等待時間。
1)時間:
所有活動的執行都是在某個時間點上執行,大部分的資料統計都需要使用到時間資料。時間可以按需求的精準程度制定時間刻度;
2)佇列數:
佇列數是等待需要處理的貨物(托盤/箱/盒)的數量,佇列數的多少體現了系統的繁忙程度;
3)服務檯數量:
一個服務檯的構成由“人、設施、裝置”組成,服務檯數量影響了服務檯同時處理佇列的能力。
資料記錄表(示例)
4、資料記錄結果
5、MATLAB原始碼:
系統執行過程的資料記錄(示例)
[M,N] =size(worksheet_raw); %定義worksheet_raw行列數
for I = 1:M-1
%到貨
if any(arrivaltable(:,1) ==worksheet_data(I,raw_wtime)) %到貨表列名稱寫入
if worksheet_data(I,raw_unload_queue)> 0
pallet_data =arrivaltable(find(arrivaltable(:,1) == worksheet_data(I,raw_wtime)),2) +worksheet_data(I,raw_unload_queue);
else
pallet_data =arrivaltable(find(arrivaltable(:,1) == worksheet_data(I,raw_wtime)),2);
end
end
pallet_data_0 = pallet_data;
if I > 1
for n = 1:worksheet_n
%%%調整卸貨資源
ifany(worksheet_data(:,raw_e_unload) == I) %假設上一行有資源釋放
deskNo =worksheet_data(find(worksheet_data(:,raw_e_unload) == I),raw_desk_unload);
% deskNo =sum(worksheet_data(:,raw_e_unload) == I - 1); %檢視服務檯數量
if deskNo > 1 %如果是多服務檯
ifany(rescall_data(:,raw_unload_per) == 0)
deskNo0 =length(find(rescall_data(:,raw_unload_per) == 0)); %找到資源表中被佔用資源座標
for desk_No = 1:deskNo
rescall_data(desk_No,raw_unload_per) = 1; %調整資源表中的卸貨人員資源狀態
rescall_data(desk_No,raw_eforklift) = 1; %調整資源表中的電叉車資源狀態
rescall_data(desk_No,raw_unloadar) = 1; %調整資源表中的卸貨埠資源狀態
end
end
elseif deskNo <= 1 %如果是單個服務檯
rescall_data(min(find(rescall_data(:,raw_unload_per) == 0,1)),raw_unload_per) = 1; %調整資源表中的卸貨人員資源狀態
rescall_data(min(find(rescall_data(:,raw_eforklift) == 0,1)),raw_eforklift) = 1; %調整資源表中的電叉車資源狀態
rescall_data(min(find(rescall_data(:,raw_unloadar) == 0,1)),raw_unloadar) = 1; %調整資源表中的卸貨埠資源狀態
end
end
end
end
%%%調整檢驗資源
if any(worksheet_data(:,raw_e_inspect) ==I) %假設本行有資源釋放
deskNo =worksheet_data(find(worksheet_data(:,raw_e_inspect) == I),raw_desk_inspect);
% deskNo =sum(worksheet_data(:,raw_e_inspect) == I); %檢視服務檯數量
if deskNo > 1 %如果是多服務檯
ifany(rescall_data(:,raw_inspect_per) == 0)
deskNo0 =length(find(rescall_data(:,raw_inspect_per) == 0)); %找到資源表中被佔用資源座標
for desk_No = 1:deskNo
rescall_data(desk_No,raw_inspect_per) = 1; %調整資源表中的卸貨人員資源狀態
end
end
elseif deskNo <= 1 %如果是單個服務檯
rescall_data(min(find(rescall_data(:,raw_inspect_per) == 0,1)),raw_inspect_per) = 1; %調整資源表中的卸貨人員資源狀態
end
end
%%%調整檢驗資源完成
%%%調整上架資源
if any(worksheet_data(:,raw_e_store) ==I) %假設本行有資源釋放
deskNo = worksheet_data(find(worksheet_data(:,raw_e_store) == I),raw_desk_store);
% deskNo =sum(worksheet_data(:,raw_e_inspect) == I); %檢視服務檯數量
if deskNo > 1 %如果是多服務檯
ifany(rescall_data(:,raw_store_per) == 0)
deskNo0 =length(find(rescall_data(:,raw_store_per) == 0)); %找到資源表中被佔用資源座標
for desk_No = 1:deskNo
rescall_data(desk_No,raw_store_per) = 1; %調整資源表中的卸貨人員資源狀態
end
end
elseif deskNo <= 1 %如果是單個服務檯
rescall_data(min(find(rescall_data(:,raw_store_per) == 0,1)),raw_store_per) = 1; %調整資源表中的卸貨人員資源狀態
end
end
%%%調整上架資源完成
worksheet_n = length(worksheet_txt);
if sum(pallet_data > 0) %判斷是否有新到貨資料 【到貨資料】
worksheet_data(I,raw_unload_queue) =sum(pallet_data); %新到貨資料進入佇列 【到貨佇列】
pallet_data(:) = 0; %到貨表清零
end
%%%卸貨流程
%%%%檢視佇列
if worksheet_data(I,raw_unload_queue) >0 %判斷pallet_data是否有到貨 【判斷到貨佇列】
%%%%構建服務檯
if length(find(rescall_data(:,raw_unload_per)> 0)) >= 1 && length(find(rescall_data(:,raw_eforklift) > 0))>= 1 && length(find(rescall_data(:,raw_unloadar) > 0)) >=1 %服務檯是否空閒 【判斷卸貨資源服務檯】
deskNo_unload =min([length(find(rescall_data(:,raw_unload_per) > 0)),length(find(rescall_data(:,raw_eforklift)> 0)),length(find(rescall_data(:,raw_unloadar) >0)),worksheet_data(I,raw_unload_queue)]); %確定服務檯數量【構建多服務檯】
worksheet_data(I,raw_desk_unload) =deskNo_unload; %記錄服務檯數量【卸貨服務檯數量】
%%%%服務
worksheet_data(I,raw_b_unload) =I; %開始卸貨時間【開始卸貨】
worksheet_data(I,raw_e_unload) = I+ res_data(raw_serviceability,raw_unload_per); %按服務能力確定結束時間【卸貨結束】
%%%%形成佇列
worksheet_data(I +1,raw_unload_queue) = worksheet_data(I,raw_unload_queue) - deskNo_unload; %佇列減少【進入服務檯】
%%%%資源佔用
for unload_i = 1:deskNo_unload %迴圈建立服務檯
rescall_data(min(find(rescall_data(:,raw_unload_per) ~= 0, 1)),raw_unload_per) = 0;
unloadper_sheet(size(unloadper_sheet,1)+ 1,1) = unload_i; %記錄卸貨人員資源監控表
unloadper_sheet(size(unloadper_sheet,1),3) = I;
unloadper_sheet(size(unloadper_sheet,1),4) = I +res_data(raw_serviceability,raw_unload_per);
rescall_data(min(find(rescall_data(:,raw_eforklift)~= 0, 1 )),raw_eforklift) = 0; %資源服務物件減一 【佔用一個卸貨服務檯資源】
eforklift_sheet(size(eforklift_sheet,1) + 1,1) = unload_i; %記錄電叉車資源監控表
eforklift_sheet(size(eforklift_sheet,1),3) = I;
eforklift_sheet(size(eforklift_sheet,1),4) = I + res_data(raw_serviceability,raw_unload_per);
rescall_data(min(find(rescall_data(:,raw_unloadar) ~= 0, 1 )),raw_unloadar)= 0; %資源服務物件減一 【佔用一個卸貨服務檯資源】
unloadar_sheet(size(unloadar_sheet,1) + 1,1) = unload_i; %記錄卸貨臺監控表
unloadar_sheet(size(unloadar_sheet,1),3) = I;
unloadar_sheet(size(unloadar_sheet,1),4) = I + res_data(raw_serviceability,raw_unload_per);
end
else
worksheet_data(I +1,raw_unload_queue) = worksheet_data(I,raw_unload_queue);
end
end
%%%卸貨流程完成
if I > 1
%%%檢驗流程
%%%%檢視並形成佇列
if any(worksheet_data(I -1,raw_inspect_queue)) %上一時間指標佇列是否有剩餘
if any(worksheet_data(I -1,raw_desk_inspect)) %如果上一行有服務檯
queue_surplus = worksheet_data(I -1,raw_inspect_queue) - worksheet_data(I - 1,raw_desk_inspect); %佇列扣除
else
queue_surplus = worksheet_data(I -1,raw_inspect_queue); %判斷佇列是否有剩餘
end
else
queue_surplus = 0;
end
if any(sum(worksheet_data(:,raw_e_unload)== I))
serviceNo_i = find((worksheet_data(:,raw_e_unload)) ==I); %檢視卸貨結束服務數量
queue_new =sum(worksheet_data(:,raw_e_unload) == I) *worksheet_data(serviceNo_i,raw_desk_unload); %貨物檢驗佇列生成
else
queue_new = 0;
end
worksheet_data(I,raw_inspect_queue) =queue_surplus + queue_new; %剩餘佇列順延
% worksheet_data(I,raw_inspect_queue) = sum(worksheet_data(:,raw_e_unload)== I);
if worksheet_data(I,raw_inspect_queue) >0 %判斷是否有佇列等待 【判斷檢驗佇列】
%%%%構建服務檯
iflength(find(rescall_data(:,raw_inspect_per) > 0)) >= 1 %服務檯是否空閒 【判斷卸貨資源服務檯】
deskNo_inspect =min([length(find(rescall_data(:,raw_inspect_per) >0)),worksheet_data(I,raw_inspect_queue)]); %確定服務檯數量【構建多服務檯】
worksheet_data(I,raw_desk_inspect)= deskNo_inspect; %記錄服務檯數量【檢驗服務檯數量】
%%%%服務
worksheet_data(I,raw_b_inspect) =I; %開始卸貨時間【開始卸貨】
worksheet_data(I,raw_e_inspect) = I+ res_data(raw_serviceability,raw_inspect_per); %按服務能力確定結束時間【卸貨結束】
worksheet_data(I +1,raw_inspect_queue) = worksheet_data(I,raw_inspect_queue) -deskNo_inspect; %佇列減少【進入服務檯】
for inspect_i =1:deskNo_inspect %迴圈建立服務檯
rescall_data(min(find(rescall_data(:,raw_inspect_per) ~= 0, 1)),raw_inspect_per) = 0; %資源服務物件減一 【佔用一個卸貨服務檯資源】
inspectper_sheet(size(inspectper_sheet,1) + 1,1) = inspect_i; %記錄卸貨人員資源監控表
inspectper_sheet(size(inspectper_sheet,1),3) = I;
inspectper_sheet(size(inspectper_sheet,1),4)= I + res_data(raw_serviceability,raw_inspect_per);
end
end
end
%%%檢驗流程完成
……