スケジューリングソルバ ScNurse

スケジューリングソルバ
ScNurse
Software
Development Kit
for Socket Interface
Technical View.
2016.Aug.5 菅原システムズ
評価開発環境
シリアライズ
(problem.json)
制約クラス
ソケットで送る
GUI C#ソース
実行エンジン
ソケットで解受け取り
1.スケジュールナースVer2相当のGUIソー
ス(C# )が付いています。
2.制約クラスの使い方は、入力であるGUI
ソースと、出力であるproblem.jsonを見る
とよいでしょう。
3.ソースをコピペするとスケジュールナー
スが出来てしまいます。(3rdParty GUI モ
ジュールを除く)
probelm.json と 解形式
{
"SO": {
"name": "Shift Objects",
"members": [
{
"use": true,
"name": "ShiftDef",
"def_name": "オンコール",
"auto_schedule": true,
"color": "Yellow",
"label": "コ",
"another_labels": [
"",
""
],
"another_colors": [
"Gray",
"Gray"
],
SAT,7,36,2016,8
1日代休,オンコール,半日代
休,外来勤務,半日代休,訪問看
護,オンコール,外来勤務,訪問
看護,外来勤務,外来勤務,休み
,外来勤務,訪問看護,オンコ...
シリアライズクラス singleA
problem.json
singleA
ScNurse
解テキスト
クラスsingleAをJSON.Netでシリアライライズしたものが、
problem.json(UTF-8) となりScNurseにソケットを通してとして渡さ
れます。
ScNurseは、求解数分のテキストをとして生成します。(UTF-8)
singleAの役割
スケジュールナースⅡの場合、プロジェクトファイルと役割を兼ねて
いるので、ソルバへのシリアライズとしては、やや冗長な処理になっ
ています。ソルバと無関係な部分は、privateにしているので、そちら
は見る必要がありません。(private部はシリアライズされません。)
singleAは、ソースファイルsc_nurse.csで記述されています。Doxgen
を見るとファイル数も多いのですが、その殆どは、singleAにユーザ
記述を設定させるためのGUI処理です。solverの設定の仕方として
は、最終的に何がsinlgeAにセットされたかが問題ですので、
sc_nurse.cs上 singleAのメンバーセット方法を見れば十分かと思いま
す。
problem.json生成(solve_page.cs)
public bool make_problem()
{
{
//Aug.03.2016
problem_server_string = form1.serialize_all();//シリアライズ
if (problem_server_string=="") return false;//Aug.03.2016
StreamWriter sw;
string path = Application.StartupPath + "¥¥problem.json";
System.Text.Encoding enc = new System.Text.UTF8Encoding(false);//UTF8変換
sw = new StreamWriter(path, false, enc);//BOMなしでないとパースエラーになる
sw.Write(problem_server_string);//Aug.03.2016
sw.Close();
}
return true;
}
form1シリアライズ部
Newtonsoft.Json.JsonSerializerSettings get_json_setting()
{
Newtonsoft.Json.JsonSerializerSettings settings = new Newtonsoft.Json.JsonSerializerSettings { Converters = { new
CustomDateTimeConverter() }, Formatting = Newtonsoft.Json.Formatting.Indented };//, TypeNameHandling =
Newtonsoft.Json.TypeNameHandling.All };
return settings;
}
public string serialize_all()
{
Newtonsoft.Json.JsonSerializerSettings settings = get_json_setting();
bool success=true;
try {
SingleA.set_projectname(most_recentfile);//プロジェクト名設定
SingleA.update_version();
success &=SingleA.eval_macro(rm);//マクロ評価
if (!success ) return "";
}
catch (Exception ex){
MessageBox.Show(ex.Message);
return "";
}
string Serialized = Newtonsoft.Json.JsonConvert.SerializeObject(SingleA, settings);//Json.NETのシリアライズ
return Serialized;
}
解読み込み部(solve_page.cs)
public string Read_Solution(int I)
{
string solution_filename = "";
solution_filename += Application.StartupPath;
solution_filename += "¥¥solution";
solution_filename += (I + 1).ToString();// solutions.get_nof_solutions().ToString();
solution_filename += ".txt";
try
{
TextFieldParser parser = new TextFieldParser(solution_filename,
System.Text.Encoding.GetEncoding("UTF-8"));
using (parser)
{
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(","); // 区切り文字はコンマ
.....
SingleA.get_schedules().label_conversion(Solution.members);//Shiftが同じでAnotherLabelが
予定入力で使われていたら予定入力に合わせる
Solution.save_as_history(SingleA);
SingleA.get_solutions().members.Add(Solution);//singleAインスタンスに解追加
ソケット受信部
private void ReadCallback(object obj)//ソケット受信(非同期)
{
try
{
using ( TcpClient client = (TcpClient)obj)
{
using (NetworkStream stream = client.GetStream())
{
const int BUFF_SIZE = 100000;//受信バッファ
byte[] readbuf = new byte[BUFF_SIZE];
int len;
int sol_counter=1;
while (true)
{
len = stream.Read(readbuf, 0, BUFF_SIZE);
if (len <= 0) break;
}else if (readbuf[len-1]!=0){//Agu.23.2015 We have more data on stream
while (true)
{
int len1 = stream.Read(readbuf,len, BUFF_SIZE - 1 - len);
if (len1 <=0) goto exit_loop;
else {
len += len1;
if (readbuf[len - 1]==0) break;
}
}
}
ソケット生成部・受信スレッド生成部
client = new TcpClient();
string[] ar = server_ip_address.ToArray();
if (ar.Length == 0)
{
richTextBox1.Focus();
richTextBox1.AppendText("計算サーバが見つかりません。内部エンジンに切
り替えます。¥n");
return false;
}
richTextBox1.Focus();
richTextBox1.AppendText("計算サーバが見つかりました。接続します。¥n");
client.Connect(ar[0], server_port);
richTextBox1.Focus();
richTextBox1.AppendText("計算サーバと接続しました。¥n");
send_tcp(write_string);//problem.jsonの送出
// 受信スレッドを作成・実行
Thread thread = new Thread(new ParameterizedThreadStart(ReadCallback));
thread.Start(client);
connect_state = ConnectState.CONNECTED;
return true;;
ソケット受信続き
string s = Encoding.UTF8.GetString(readbuf, 0, len);
string[] stArrayData = s.Split('¥0');
for (int i = 0; i < stArrayData.Length;i++ )
{
string str = stArrayData[i];
if (str.Length < 1) continue;
if (str.IndexOf("SAT") >= 0 || str.IndexOf("UNSAT") >= 0)//解の切り出し
{
int rows = SingleA.get_active_staffs();
string path = "";
path += Application.StartupPath + "¥¥solution";
path += sol_counter.ToString();
path += ".txt";
path += "";
StreamWriter sw = new StreamWriter(path);
sw.WriteLine(str);
sol_counter++;
sw.Close();
}
else
{//解でなければ求解状況報告
AppendText(str);
}
}
}
exit_loop:
//切断されるとここにくる
connect_state = ConnectState.CONNECTED_EXIT;
stream.Close();
client.Close();
}
}
}
catch (Exception ex)
{
connect_state = ConnectState.CONNECTED_EXIT;
MessageBox.Show(ex.Message);
}
ソケット送信部
//Socket送信
void send_tcp(string write_string)
{
if (client != null)
{
NetworkStream stream = client.GetStream();
string send_string = "TAKAYUKI
write_string;//32bytes
byte[] writebuf =
Encoding.UTF8.GetBytes(send_string);
Array.Resize(ref writebuf, writebuf.Length + 1);
writebuf[writebuf.Length - 1] = 0;//Null Terminal
stream.Write(writebuf, 0, writebuf.Length);
stream.Flush();
}
}
"+
求解中止処理
private void button2_Click(object sender, EventArgs e)
{
#if USE_EXTERNAL_SERVER
try
{
send_tcp("Abrot");
return;
}
catch (Exception ex)
{
}
#endif
singleA 構造図
日
設定群
シフト
schedules
制約群
求解パラメータ
solving_
parameters
スタッフ
column_
constraints
date_objects
date_object
date_aggregates
date_aggregate_class
shift_objects
shift_object
shift_aggregates
shift_aggregate_class
group_properties
group_property
group_aggregates
group_aggregate
staff_properties
staff_property
column_constraints_group
column_constraint
row_
constraints
row_constraints_group
row_constraint
pair_
constraints
pair_constraints_group
pair_constraint
singleA 最低限設定が必要なオブジェクト群
date_objects
日
設定群
シフト
date_object
既定オブジェクト群
,days,target_year,target_month
shift_objects
shift_object
staff_properties
staff_property
スタッフ数、日数
schedules
制約群
求解パラメータ
solving_
parameters
スタッフ
その他は、インスタンスとしては
必要ですが、ブランクでも支障あ
りません。initialize部参照
solving_parametersクラス
求解パラメータ
public Dictionary<string, int> parameters =new Dictionary<string,int>();
public void initialize(singleA SingleA, System.Resources.ResourceManager rm)
{
parameters["NofCpus"]=1; //CPU数
parameters["N_of_solutions"]=1;//求解数
parameters["not_repeat_planned_errors"]=1;//予定エラーの再掲を防ぐ
parameters["BestEffots"]=1; //最小化有効
parameters["HardTimeout"]=30; //ハードタイムアウト(秒)
parameters["ErrorAnalysis"]=1; // エラー原因解析
parameters["SoftTimeout"]=10; // ソフトタイムアウト(秒)
parameters["BestEffotsTimeout"]=5;//default:5
parameters["UseExternalConstraint"]=0;//言語制約使用
}
ソフト制約設定 ソフト制約レベル1-7
public Dictionary<int, Dictionary<string, sw_int> > solving_map=new Dictionary<int,Dictionary<string,sw_int>>();
"column" ➡列
"row"
➡行
"planned" ➡予定
"ext"
➡外部
bool use = solving_map[level][s].use;
int per_errors = solving_map[level][s].value;
int total_allowerable_errors = solving_map[level][s].total_max_errors;
詳細仕様
・Doxygen生成文書・ソースコードをご参照ください。
・GUI各データグリッド(表)とsingleA内クラスは、ほぼ
一対一対応しています。
・スケジュールナースⅡチュートリアル・マニュアルも併
せてお読みください。
Visual Studioプロジェクト
ファイル群には、Visual Studio2015update3プ
ロジェクトファイル、実行ファイル、DLL等が含まれます。
これにより、ほぼスケジュールナースⅡGUIの開発環境が再
現できます。(C#デバッガが使えます。)これを出発点とし
て、貴社アプリインタフェースの開発が可能になります。
スケジュールナースⅡ(C#)
ソケット
貴アプリ(Java,C#,Javascript,Python..)
C#ソースの必要箇所
を移植
ScNurse
評価用外部サーバ(V
PS Linux)
ソルバ性能、インタフェースの
評価で、目途がたったら、貴ア
ププラットフォームのLinu
x/Windows・(32/64ビット)
評価用エンジンを提供します。
Visual Studio Project インストール
圧縮ファイルを次のフォルダに展開します。
ScNurseSDKを起動します。
ダブルクリック
schedule_nurse2 プロジェクト
下図のようになります。
起動の確認
起動の確認を行います。
サーバからのメッセージを
ご確認ください。
ビルドの確認
WindowsFormsApplication1.exe
Exeファイルが次のフォルダに生成されたら成功です。
オンラインサポート
ご要望があればオンラインサポートが可能です。その日の午前
中にメールを頂ければ、その日中に対応可能です。SKYPE
で画面を共有しながらお話しが可能です。インストールの必要
ないSKYPE MEETINGでもお打合せが可能です。
評価期間
• 1か月です。1か月を過ぎると起動できなくなります。
• お申し出があった場合に3か月まで更新が出来ます
。ただし、2か月目以降は有償となります。
以上